diff --git a/.acrolinx-config.edn b/.acrolinx-config.edn new file mode 100644 index 000000000..c3cbcdd23 --- /dev/null +++ b/.acrolinx-config.edn @@ -0,0 +1,2 @@ +{:allowed-branchname-matches ["main" "master" "release-.*"] + :allowed-filename-matches ["articles"]} diff --git a/.gitignore b/.gitignore index fdfaa1a77..41c6d8551 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ articles/directories.txt _themes*/ .openpublishing.buildcore.ps1 +/.vs +.vscode/settings.json diff --git a/.openpublishing.build.ps1 b/.openpublishing.build.ps1 deleted file mode 100644 index aadef7620..000000000 --- a/.openpublishing.build.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -param( - [string]$buildCorePowershellUrl = "https://opbuildstorageprod.blob.core.windows.net/opps1container/.openpublishing.buildcore.ps1", - [string]$parameters -) -# Main -$errorActionPreference = 'Stop' - -# Step-1: Download buildcore script to local -echo "download build core script to local with source url: $buildCorePowershellUrl" -$repositoryRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition -$buildCorePowershellDestination = "$repositoryRoot\.openpublishing.buildcore.ps1" -Invoke-WebRequest $buildCorePowershellUrl -OutFile "$buildCorePowershellDestination" - -# Step-2: Run build core -echo "run build core script with parameters: $parameters" -& "$buildCorePowershellDestination" "$parameters" -exit $LASTEXITCODE diff --git a/.openpublishing.publish.config.json b/.openpublishing.publish.config.json index 2b33ef934..036718bc1 100644 --- a/.openpublishing.publish.config.json +++ b/.openpublishing.publish.config.json @@ -1,154 +1,186 @@ { - "docsets_to_publish":[ - { - "docset_name":"bot-framework", - "build_source_folder":"articles", - "build_output_subfolder":"bot-framework", - "locale":"en-us", - "monikers":[], - "moniker_ranges":[ - ">= azure-bot-service-3.0", - ">= botbuilder-3.12.2.4", - ">= skypebotsmedia-1.5.0.1177-alpha", - ">= botconnector-3.11.1" - ], - "open_to_public_contributors":true, - "type_mapping":{ - "Conceptual":"Content", - "ManagedReference":"Content", - "RestApi":"Content" - }, - "build_entry_point":"docs", - "template_folder":"_themes" - }, - { - "docset_name":"bot-framework-rest-api", - "build_source_folder":"rest", - "build_output_subfolder":"bot-framework-rest-api", - "locale":"en-us", - "monikers":[], - "open_to_public_contributors":false, - "type_mapping":{ - "Conceptual":"Content", - "ContextObject":"Toc", - "ManagedReference":"Content", - "RestApi":"Content" - }, - "build_entry_point":"docs", - "template_folder":"_themes", - "version":0 - }, - { - "docset_name":"bot-framework-dotnet-api", - "build_source_folder":"dotnet", - "build_output_subfolder":"bot-framework-dotnet-api", - "locale":"en-us", - "open_to_public_contributors":true, - "type_mapping":{ - "Conceptual":"Content", - "ManagedReference":"Content", - "RestApi":"Content", - "NetEnum":"Content", - "NetDelegate":"Content", - "NetNamespace":"Content", - "NetMember":"Content", - "NetType":"Content" - }, - "build_entry_point":"docs", - "template_folder":"_themes", - "customized_tasks":{ - "docset_postbuild":[ - "_dependentPackages/ECMA2Yaml/tools/PostBuild.ps1" - ], - "docset_prebuild":[ - "_dependentPackages/ECMA2Yaml/tools/Run.ps1", - "_dependentPackages/CommonPlugins/tools/SplitTOC.ps1", - "_dependentPackages/CommonPlugins/tools/DiffFolder.ps1" - ] - } - } - ], - "notification_subscribers":[ - "3b0e6dd6.microsoft.com@amer.teams.ms", - "botframeworkcontentteam@service.microsoft.com" - ], - "sync_notification_subscribers":[], - "branches_to_filter":[], - "git_repository_url_open_to_public_contributors":"https://github.com/MicrosoftDocs/bot-docs", - "git_repository_branch_open_to_public_contributors":"live", - "skip_source_output_uploading":false, - "need_preview_pull_request":true, - "contribution_branch_mappings":{}, - "dependent_repositories":[ - { - "path_to_root":"_themes", - "url":"https://github.com/Microsoft/templates.docs.msft", - "branch":"master", - "branch_mapping":{} + "docsets_to_publish": [ + { + "docset_name": "bot-framework", + "build_source_folder": "articles", + "build_output_subfolder": "bot-framework", + "locale": "en-us", + "monikers": [], + "moniker_ranges": [ + ">= azure-bot-service-3.0", + ">= botbuilder-3.12.2.4", + ">= skypebotsmedia-1.5.0.1177-alpha", + ">= botconnector-3.11.1" + ], + "type_mapping": { + "Conceptual": "Content", + "ManagedReference": "Content", + "NetEnum": "Content", + "NetDelegate": "Content", + "NetNamespace": "Content", + "NetMember": "Content", + "NetType": "Content", + "RestApi": "Content" }, - { - "path_to_root":"_themes.pdf", - "url":"https://github.com/Microsoft/templates.docs.msft.pdf", - "branch":"master", - "branch_mapping":{} + "build_entry_point": "docs", + "template_folder": "_themes", + "customized_tasks": { + "docset_prebuild": [ + "_dependentPackages/ECMA2Yaml/tools/Run.ps1", + "_dependentPackages/CommonPlugins/tools/SplitTOC.ps1", + "_dependentPackages/CommonPlugins/tools/DiffFolder.ps1" + ], + "docset_postbuild": [ + "_dependentPackages/ECMA2Yaml/tools/PostBuild.ps1" + ] }, - { - "path_to_root":"botbuilder-samples", - "url":"https://github.com/Microsoft/BotBuilder-Samples", - "branch":"master", - "branch_mapping":{} + "ECMA2Yaml": { + "SourceXmlFolder": "dotnet/xml", + "OutputYamlFolder": "dotnet/api", + "Flatten": true, + "id": "dotnet-template", + "SDPMode": true }, - { - "path_to_root":"botbuilder-python", - "url":"https://github.com/microsoft/BotBuilder-Samples", - "branch":"master", - "branch_mapping":{} - } - ], - "branch_target_mapping":{ - "live":[ - "Publish", - "Pdf" + "SplitTOC": [ + "dotnet/api/dotnet-template/toc.yml" ], - "master":[ - "Publish", - "Pdf" + "DiffFolder": [ + "dotnet/api" ] - }, - "need_generate_pdf_url_template":true, - "Targets":{ - "Pdf":{ - "template_folder":"_themes.pdf" - } - }, - "need_generate_intellisense":false, - "template_folder":"_themes", - "dependent_packages":[ - { - "path_to_root":"_dependentPackages/CommonPlugins", - "target_framework":"net45", - "version":"latest", - "id":"Microsoft.OpenPublishing.CommonPlugins", - "nuget_feed":"https://www.myget.org/F/op/api/v2" + }, + { + "docset_name": "bot-framework-rest-api", + "build_source_folder": "rest", + "build_output_subfolder": "bot-framework-rest-api", + "locale": "en-us", + "monikers": [], + "open_to_public_contributors": false, + "type_mapping": { + "Conceptual": "Content", + "ManagedReference": "Content", + "NetEnum": "Content", + "NetDelegate": "Content", + "NetNamespace": "Content", + "NetMember": "Content", + "NetType": "Content", + "ContextObject": "Toc", + "RestApi": "Content" + }, + "build_entry_point": "docs", + "template_folder": "_themes", + "customized_tasks": { + "docset_prebuild": [ + "_dependentPackages/ECMA2Yaml/tools/Run.ps1", + "_dependentPackages/CommonPlugins/tools/SplitTOC.ps1", + "_dependentPackages/CommonPlugins/tools/DiffFolder.ps1" + ], + "docset_postbuild": [ + "_dependentPackages/ECMA2Yaml/tools/PostBuild.ps1" + ] }, - { - "id":"Microsoft.DocAsCode.ECMA2Yaml", - "nuget_feed":"https://www.myget.org/F/op/api/v2", - "path_to_root":"_dependentPackages/ECMA2Yaml", - "target_framework":"net45", - "version":"latest" - } - ], - "ECMA2Yaml":{ - "SourceXmlFolder":"dotnet/xml", - "OutputYamlFolder":"dotnet/api", - "Flatten":true, - "SDPMode":true - }, - "DiffFolder":[ - "dotnet/api" - ], - "SplitTOC":[ - "dotnet/api/toc.yml" - ] + "ECMA2Yaml": { + "SourceXmlFolder": "dotnet/xml", + "OutputYamlFolder": "dotnet/api", + "Flatten": true, + "id": "dotnet-template", + "SDPMode": true + }, + "SplitTOC": [ + "dotnet/api/dotnet-template/toc.yml" + ], + "DiffFolder": [ + "dotnet/api" + ], + "version": 0 + } + ], + "notification_subscribers": [ + "3b0e6dd6.microsoft.com@amer.teams.ms", + "botframeworkcontentteam@service.microsoft.com" + ], + "sync_notification_subscribers": [], + "branches_to_filter": [], + "skip_source_output_uploading": false, + "need_preview_pull_request": true, + "contribution_branch_mappings": {}, + "dependent_repositories": [ + { + "path_to_root": "_themes", + "url": "https://github.com/Microsoft/templates.docs.msft", + "branch": "main", + "branch_mapping": {} + }, + { + "path_to_root": "_themes.pdf", + "url": "https://github.com/Microsoft/templates.docs.msft.pdf", + "branch": "main", + "branch_mapping": {} + }, + { + "path_to_root": "botbuilder-samples", + "url": "https://github.com/Microsoft/BotBuilder-Samples", + "branch": "main", + "branch_mapping": {} + }, + { + "path_to_root": "botframework-sdk", + "url": "https://github.com/microsoft/botframework-sdk", + "branch": "main", + "branch_mapping": {} + }, + { + "path_to_root": "articles/reusable-content", + "url": "https://github.com/MicrosoftDocs/reusable-content", + "branch": "main", + "branch_mapping": {} + }, + { + "path_to_root": "azure-reference-other-repo", + "url": "https://github.com/MicrosoftDocs/azure-reference-other-pr", + "branch": "main", + "branch_mapping": {} + } + ], + "branch_target_mapping": { + "live": [ + "Publish", + "Pdf" + ], + "main": [ + "Publish", + "Pdf" + ] + }, + "need_generate_pdf_url_template": true, + "targets": { + "pdf": { + "template_folder": "_themes.pdf" + } + }, + "docs_build_engine": { + "name": "docfx_v3" + }, + "need_generate_pdf": false, + "need_generate_intellisense": false, + "dependent_packages": [ + { + "path_to_root": "_dependentPackages/CommonPlugins", + "target_framework": "net45", + "version": "latest", + "id": "Microsoft.OpenPublishing.CommonPlugins", + "nuget_feed": "https://www.myget.org/F/op/api/v2" + }, + { + "id": "Microsoft.DocAsCode.ECMA2Yaml", + "nuget_feed": "https://www.myget.org/F/op/api/v2", + "path_to_root": "_dependentPackages/ECMA2Yaml", + "target_framework": "net45", + "version": "latest" + } + ], + "xref_query_tags": [ + "/dotnet", + "/uwp/api" + ], + "template_folder": "_themes" } diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 90fbd3048..de33dbfd4 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -1,704 +1,2088 @@ { "redirections": [ { - "source_path": "articles/bot-service-quickstart.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/abs-quickstart", + "source_path": "articles/bot-service-debug-adaptive-tools.md", + "redirect_url": "/azure/bot-service/file-format/bot-builder-lg-file-format", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-channel-connect-telephony.md", + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-channel-connect-kik.md", + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-tutorial-dispatch.md", + "redirect_url": "/azure/bot-service/bot-builder-concept-luis", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-activity-processing.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-manage-analytics.md", + "redirect_url": "/azure/bot-service/bot-service-manage-overview", + "redirect_document_id": false + }, + { + "source_path": "articles/tutorial-provision-a-bot.md", + "redirect_url": "/azure/bot-service/provision-and-publish-a-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/tutorial-publish-a-bot.md", + "redirect_url": "/azure/bot-service/provision-and-publish-a-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-tutorial-add-qna.md", + "redirect_url": "/azure/bot-service/provision-and-publish-a-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-builder-deploy-az-cli.md", + "redirect_url": "/azure/bot-service/provision-and-publish-a-bot", "redirect_document_id": true }, + { + "source_path": "articles/v4sdk/migration/migration-overview.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/migration-about.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/net-migration-quickreference.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/conversion-framework.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/conversion-core.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/csharp-user-state-using.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/migration-about-javascript.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/javascript-migration-quickreference.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/conversion-javascript.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/javascript-user-state-using.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/convert-to-skill-overview.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/net-v3-as-skill.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/migration/javascript-v3-as-skill.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/language-generation/bot-builder-howto-use-lg-custom-functions.md", + "redirect_url": "/azure/bot-service/bot-builder-concept-language-generation", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-adapter-connect-webex.md", + "redirect_url": "/azure/bot-service/bot-service-channel-additional-channels", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-tutorial-deploy-basic-bot.md", + "redirect_url": "/azure/bot-service/tutorial-provision-a-bot", + "redirect_document_id": false + }, + { + "source_path": "java/bot-builder-java-samples.md", + "redirect_url": "/azure/bot-service/index-bf-sdk", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/skills-about-skill-bots.md", + "redirect_url": "/azure/bot-service/skills-conceptual", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-file-basics.md", + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false + }, + { + "source_path": "articles/security-baseline.md", + "redirect_url": "/security/benchmark/azure/baselines/bot-service-security-baseline", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-cross-train.md", + "redirect_url": "/composer/concept-language-understanding", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-dialogs-declarative.md", + "redirect_url": "/composer/concept-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-handle-user-interrupts-adaptive.md", + "redirect_url": "/composer/how-to-ask-for-user-input", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-mixed-dialogs.md", + "redirect_url": "/composer/concept-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-dialogs-adaptive.md", + "redirect_url": "/composer/quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-adaptive-dialog-setup.md", + "redirect_url": "/composer/quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-bf-cli-deploy-luis.md", + "redirect_url": "/composer/concept-natural-language-processing", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-bf-cli-update-luis.md", + "redirect_url": "/composer/concept-natural-language-processing", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-bf-cli-deploy-qna.md", + "redirect_url": "/composer/concept-natural-language-processing", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-howto-bf-cli-alterations-qna.md", + "redirect_url": "/composer/concept-natural-language-processing", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-adaptive-dialog-Introduction.md", + "redirect_url": "/composer/concept-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-Triggers.md", + "redirect_url": "/composer/concept-dialog#anatomy-of-a-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-Actions.md", + "redirect_url": "/composer/concept-dialog#anatomy-of-a-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-Inputs.md", + "redirect_url": "/composer/concept-memory#set-properties-with-prompts", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-recognizers.md", + "redirect_url": "/composer/concept-dialog#anatomy-of-a-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-generators.md", + "redirect_url": "/composer/concept-dialog#anatomy-of-a-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-memory-states.md", + "redirect_url": "/composer/concept-memory", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-interruptions.md", + "redirect_url": "/composer/concept-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-adaptive-dialog-declarative.md", + "redirect_url": "/composer/concept-dialog", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-concept-cross-train.md", + "redirect_url": "/composer/concept-natural-language-processing", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-builder-tutorial-authentication.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-builder-tutorial-authentication", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-activities-entities.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-activities-entities", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-adapter-connect-webex.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-adapter-connect-webex", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-additional-channels.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-additional-channels", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-alexa.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-alexa", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-cortana.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-cortana", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-directline.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-directline", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-directlinespeech.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-directlinespeech", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-email.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-email", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-facebook.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-facebook", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-groupme.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-groupme", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-kik.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-kik", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-line.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-line", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-skype.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-skype", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-skypeforbusiness.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-skypeforbusiness", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-slack.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-slack", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-telegram.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-telegram", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-twilio.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-twilio", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-webchat-speech.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-webchat-speech", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-webchat.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-webchat", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-connect-wechat.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-connect-wechat", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension-net-bot.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension-net-bot", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension-net-client.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension-net-client", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension-node-bot.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension-node-bot", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension-vnet.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension-vnet", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension-webchat-client.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension-webchat-client", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline-extension.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline-extension", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channel-directline.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channel-directline", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-channels-reference.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-channels-reference", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-compliance.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-compliance", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-concept-intelligence.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-concept-templates.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-concept-templates", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-debug-bot-v3.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-debug-bot-v3", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-debug-cortana-skill.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-debug-cortana-skill", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-debug-emulator.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-debug-emulator", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-debug-inspection-middleware.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-debug-inspection-middleware", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-conversation-flow.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-conversation-flow", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-first-interaction.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-first-interaction", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-navigation.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-navigation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-pattern-embed-app.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-pattern-embed-app", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-pattern-embed-web-site.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-pattern-embed-web-site", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-pattern-integrate-browser.md", + "redirect_url": "/azure/bot-service/bot-builder-concept-authentication-types", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-pattern-knowledge-base.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-pattern-knowledge-base", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-pattern-task-automation.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-pattern-task-automation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-principles.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-principles", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-design-user-experience.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-design-user-experience", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-encryption.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-encryption", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-manage-analytics.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-manage-analytics", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-manage-channels.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-manage-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-manage-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-manage-settings.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-manage-settings", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-manage-speech-priming.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-manage-speech-priming", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-migrate-bot.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-migrate-bot", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-overview-introduction.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-overview-introduction", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-overview-readme.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-overview-readme", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-app-insights-keys.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-app-insights-keys", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-bot-framework-faq.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-bot-framework-faq", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-faq-availability.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-faq-availability", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-faq-azure.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-faq-azure", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-faq-ecosystem.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-faq-ecosystem", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-faq-general.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-faq-general", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-faq-security.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-faq-security", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-identifiers-guide.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-identifiers-guide", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-links-help.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-links-help", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-resources-user-agent.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-resources-user-agent", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-review-guidelines.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-review-guidelines", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-commerce.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-commerce", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-cortana-skill.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-cortana-skill", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-enterprise-productivity.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-enterprise-productivity", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-informational.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-informational", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-internet-things.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-internet-things", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-scenario-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-scenario-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-troubleshoot-500-errors.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-troubleshoot-500-errors", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-troubleshoot-authentication-problems.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-troubleshoot-authentication-problems", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-troubleshoot-bot-configuration.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-troubleshoot-bot-configuration", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-troubleshoot-general-problems.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-troubleshoot-general-problems", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/bot-service-troubleshoot-index.md", + "redirect_url": "/previous-versions/azure/bot-service/bot-service-troubleshoot-index", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/channel-connect-teams.md", + "redirect_url": "/previous-versions/azure/bot-service/channel-connect-teams", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/conversation-designer/conversation-designer-adaptive-cards.md", + "redirect_url": "/previous-versions/azure/bot-service/conversation-designer/conversation-designer-adaptive-cards", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-activities.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-activities", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-add-input-hints.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-add-input-hints", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-add-media-attachments.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-add-media-attachments", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-add-rich-card-attachments.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-add-rich-card-attachments", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-add-suggested-actions.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-add-suggested-actions", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-audio-calls.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-audio-calls", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-channeldata.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-channeldata", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-connector.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-connector", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-cortana-skill.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-cortana-skill", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-create-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-create-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-dialogs.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-dialogs", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow-advanced.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow-advanced", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow-formbuilder.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow-formbuilder", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow-json-schema.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow-json-schema", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow-localize.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow-localize", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow-pattern-language.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow-pattern-language", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-formflow.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-formflow", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-global-handlers.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-global-handlers", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-luis-dialogs.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-luis-dialogs", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-manage-conversation-flow.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-manage-conversation-flow", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-middleware.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-middleware", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-proactive-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-proactive-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-request-payment.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-request-payment", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-scorable-dialogs.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-scorable-dialogs", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-search-azure.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-search-azure", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-security.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-security", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-state-azure-cosmosdb.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-state-azure-cosmosdb", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-state-azure-table-storage.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-state-azure-table-storage", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-state.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-state", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-text-to-speech.md", + "redirect_url": "/previous-versions/azure/bot-service/dotnet/bot-builder-dotnet-text-to-speech", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-backchannel.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-backchannel", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-conduct-audio-calls.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-conduct-audio-calls", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-cortana-skill.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-cortana-skill", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-actions.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-actions", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-prompt.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-prompt", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-replace.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-replace", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-dialog-waterfall.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-dialog-waterfall", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-handle-conversation-events.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-handle-conversation-events", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-intercept-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-intercept-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-localization.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-localization", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-message-create.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-message-create", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-proactive-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-proactive-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-recognize-intent-luis.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-luis", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-recognize-intent-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-request-payment.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-request-payment", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-search-azure.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-search-azure", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-send-input-hints.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-send-input-hints", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-send-receive-attachments.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-send-receive-attachments", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-send-rich-cards.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-send-rich-cards", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-send-suggested-actions.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-send-suggested-actions", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-send-typing-indicator.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-send-typing-indicator", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-state-azure-cosmosdb.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-state-azure-cosmosdb", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-state-azure-table-storage.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-state-azure-table-storage", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-state.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-state", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/bot-builder-nodejs-text-to-speech.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/bot-builder-nodejs-text-to-speech", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/nodejs/cortana-skill-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/nodejs/cortana-skill-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-add-input-hints.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-add-input-hints", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-add-media-attachments.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-add-media-attachments", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-add-rich-cards.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-add-rich-cards", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-add-suggested-actions.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-add-suggested-actions", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-api-reference.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-authentication.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-authentication", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-channeldata.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-channeldata", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-create-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-create-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-quickstart.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-quickstart", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-send-and-receive-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-send-and-receive-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-connector-text-to-speech.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-connector-text-to-speech", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-api-reference.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-api-reference", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-authentication.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-authentication", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-receive-messages", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-send-message.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-send-message", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-1-1-start-conversation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-api-reference.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-api-reference", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-authentication.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-concepts.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-end-conversation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-receive-activities", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-send-activity.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-send-activity", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-start-conversation", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-overview.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-overview", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/rest-api/bot-framework-rest-state.md", + "redirect_url": "/previous-versions/azure/bot-service/rest-api/bot-framework-rest-state", + "redirect_document_id": false, + "monikers": ["azure-bot-service-3.0"] + }, + { + "source_path": "articles/v4sdk/bf-cli-reference.md", + "redirect_url": "https://github.com/microsoft/botframework-cli/tree/master/packages/cli#readme", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-custom-assistant-introduction.md", + "redirect_url": "https://microsoft.github.io/botframework-solutions/index", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-resources-upgrade-to-v3.md", + "redirect_url": "/azure/bot-service/migration/migration-overview", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-conversations.md", + "redirect_url": "/azure/bot-service/bot-service-design-conversation-flow", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-quickstart.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false + }, { "source_path": "articles/azure-bot-service-build-options.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-build-online-code-editor", + "redirect_url": "/azure/bot-service/bot-service-build-online-code-editor", "redirect_document_id": true }, { "source_path": "articles/azure-bot-service-continuous-deployment.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-continuous-integration.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", "redirect_document_id": true }, { "source_path": "articles/azure-bot-service-debug-bot.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-bot", + "redirect_url": "/azure/bot-service/bot-service-debug-bot", "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-hosting-plan.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-overview-readme#hosting-plans", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-overview-readme#hosting-plans", + "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-migration-plans.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-change-hosting-plan", + "redirect_url": "/azure/bot-service/bot-service-change-hosting-plan", "redirect_document_id": true }, { "source_path": "articles/azure-bot-service-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction", + "redirect_url": "/azure/bot-service/bot-service-overview-introduction", "redirect_document_id": true }, { "source_path": "articles/azure-bot-service-quickstart.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-serverless-template-basic.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-bot", + "redirect_url": "/azure/bot-service/bot-service-debug-bot", "redirect_document_id": true }, { "source_path": "articles/azure-bot-service-serverless-template-form.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-bot", + "redirect_url": "/azure/bot-service/bot-service-debug-bot", "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-template-language-understanding.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-templates#language-understanding-bot", + "redirect_url": "/azure/bot-service/bot-service-concept-templates#language-understanding-bot", "redirect_document_id": false - }, + }, { "source_path": "articles/azure-bot-service-template-proactive.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-templates#proactive-bot", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-concept-templates#proactive-bot", + "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-template-question-answer.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-templates#question-and-answer-bot", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-concept-templates#question-and-answer-bot", + "redirect_document_id": false }, { "source_path": "articles/azure-bot-service-templates.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-templates", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-concept-templates", + "redirect_document_id": false }, { "source_path": "articles/bot-builder-howto-deploy-azure.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", "redirect_document_id": false }, { "source_path": "articles/bot-builder-overview-getstarted.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false }, { "source_path": "articles/bot-builder-reference-overview.md", "redirect_url": "https://aka.ms/botframework-v3-cs-sdk", - "redirect_document_id": true + "redirect_document_id": false }, { "source_path": "articles/bot-builder-samples.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md", - "redirect_document_id": true + "redirect_document_id": false + }, + { + "source_path": "articles/python/bot-builder-python-samples.md", + "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md", + "redirect_document_id": false }, { "source_path": "articles/bot-builder-sdk-download.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-quickstart", + "redirect_document_id": false }, { "source_path": "articles/bot-builder-tools-az-cli.md", "redirect_url": "/azure/bot-service/index", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/bot-builder-tutorial-persist-user-inputs.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-howto-v4-state", + "redirect_document_id": true }, { "source_path": "articles/bot-design-best-practices.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-principles", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-design-principles", + "redirect_document_id": false + }, { "source_path": "articles/bot-design-conversation-flow.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-conversation-flow", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-design-conversation-flow", + "redirect_document_id": true }, { "source_path": "articles/bot-design-first-interaction.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-first-interaction", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-first-interaction", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-navigation.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-navigation", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-navigation", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-pattern-embed-app.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-embed-app", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-embed-app", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-pattern-embed-web-site.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-embed-web-site", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-embed-web-site", + "redirect_document_id": false + }, { "source_path": "articles/bot-design-pattern-handoff-human.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-handoff-human", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-design-pattern-handoff-human", + "redirect_document_id": true }, { "source_path": "articles/bot-design-pattern-integrate-browser.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-integrate-browser", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-integrate-browser", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-pattern-knowledge-base.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-knowledge-base", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-knowledge-base", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-pattern-task-automation.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-task-automation", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-task-automation", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-principles.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-principles", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-principles", + "redirect_document_id": true + }, { "source_path": "articles/bot-design-user-experience.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-user-experience", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-user-experience", + "redirect_document_id": true + }, { "source_path": "articles/bot-service-design-best-practices.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-principles", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-design-principles", + "redirect_document_id": false + }, { "source_path": "articles/channel-connect-bing.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-bing", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-bing", + "redirect_document_id": false + }, { "source_path": "articles/channel-connect-cortana.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-cortana", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-cortana", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-directline.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-directline", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-directline", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-email.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-email", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-email", + "redirect_document_id": true + }, { "source_path": "articles/cchannel-connect-facebook.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-facebook", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-facebook", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-groupme.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-groupme", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-groupme", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-kik.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-kik", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-kik", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-skypeforbusiness.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-skypeforbusiness", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-skypeforbusiness", + "redirect_document_id": true + }, { - "source_path": "articles/channel-connect-slack.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-slack", - "redirect_document_id": true - }, + "source_path": "articles/channel-connect-slack.md", + "redirect_url": "/azure/bot-service/bot-service-channel-connect-slack", + "redirect_document_id": true + }, { "source_path": "articles/intelligent-bots.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/channel-connect-telegram.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-telegram", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-telegram", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-webchat-speech.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-webchat-speech", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-webchat-speech", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-webchat.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-webchat", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-webchat", + "redirect_document_id": true + }, { "source_path": "articles/channel-connect-twilio.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-connect-twilio", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-connect-twilio", + "redirect_document_id": true + }, { "source_path": "articles/cognitive-services-add-bot-knowledge.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/cognitive-services-add-bot-location-control.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/cognitive-services-add-bot-language.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/overview-how-bot-framework-works.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-overview-readme", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-overview-readme", + "redirect_document_id": false + }, { "source_path": "articles/ccognitive-services-add-bot-search.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/cognitive-services-add-bot-speech.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/cognitive-services-add-bot-vision.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/cognitive-services-bot-intelligence-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": false + }, { "source_path": "articles/debug-bots-cortana-skill-invoke.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-cortana-skill", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-debug-cortana-skill", + "redirect_document_id": true + }, { "source_path": "articles/debug-bots-emulator.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-emulator", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-debug-emulator", + "redirect_document_id": true + }, { "source_path": "articles/debug-bots-locally-vscode.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-bot", + "redirect_url": "/azure/bot-service/bot-service-debug-bot", "redirect_document_id": false - }, + }, { "source_path": "articles/deploy-dotnet-bot-visual-studio.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-continuous-deployment", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-continuous-deployment", + "redirect_document_id": false + }, { "source_path": "articles/deploy-bot-github.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-build-continuous-deployment", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-build-continuous-deployment", + "redirect_document_id": false + }, { "source_path": "articles/deploy-bot-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-build-continuous-deployment", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-build-continuous-deployment", + "redirect_document_id": false + }, { "source_path": "articles/deploy-bot-verview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-concept-intelligence", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-concept-intelligence", + "redirect_document_id": true + }, { "source_path": "articles/deploy-bot-visual-studio.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/deploy-dotnet-bot-visual-studio", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/deploy-dotnet-bot-visual-studio", + "redirect_document_id": true + }, { "source_path": "articles/overview-introduction-bot-framework.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-overview-introduction", + "redirect_document_id": false + }, { "source_path": "articles/portal-analytics-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-manage-analytics", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-manage-analytics", + "redirect_document_id": true + }, { "source_path": "articles/portal-bot-review-guidelines.md", "redirect_url": "/azure/bot-service/index", - "redirect_document_id": true - }, + "redirect_document_id": false + }, { "source_path": "articles/portal-channel-inspector.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-channel-inspector", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-channel-inspector", + "redirect_document_id": true + }, { "source_path": "articles/portal-configure-channels.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": true + }, { "source_path": "articles/portal-register-bot.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart-registration", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-quickstart-registration", + "redirect_document_id": true + }, { "source_path": "articles/deploy-nodejs-bot-visual-studio.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-continuous-deployment", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-continuous-deployment", + "redirect_document_id": true + }, { "source_path": "articles/embed-chat-control-web-page.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-design-pattern-embed-web-site", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-design-pattern-embed-web-site", + "redirect_document_id": true + }, { "source_path": "articles/portal-submit-bot-directory.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false + }, { "source_path": "articles/publish-bot-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false + }, { "source_path": "articles/reference-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-reference-overview", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-builder-reference-overview", + "redirect_document_id": true + }, { "source_path": "articles/resources-app-insights-keys.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-resources-app-insights-keys", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-resources-app-insights-keys", + "redirect_document_id": true + }, { "source_path": "articles/resources-bot-framework-faq.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-reference-overview", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-builder-reference-overview", + "redirect_document_id": false + }, { "source_path": "articles/resources-identifiers-guide.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-servicebot-service-resources-identifiers-guide", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-servicebot-service-resources-identifiers-guide", + "redirect_document_id": false + }, { "source_path": "articles/resources-links-help.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-resources-links-help", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-resources-links-help", + "redirect_document_id": true + }, { "source_path": "articles/resources-support.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-resources-links-help", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-resources-links-help", + "redirect_document_id": false + }, { "source_path": "articles/resources-tools-downloads.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": false - }, + "redirect_url": "/azure/bot-service/bot-service-quickstart", + "redirect_document_id": false + }, { "source_path": "articles/resources-upgrade-to-v3.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-resources-upgrade-to-v3", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-resources-upgrade-to-v3", + "redirect_document_id": true + }, { "source_path": "articles/resources-user-agent.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-resources-user-agent", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-resources-user-agent", + "redirect_document_id": true + }, { "source_path": "articles/rest-api-reference.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference", + "redirect_document_id": true + }, { "source_path": "articles/troubleshoot-authentication-problems.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-troubleshoot-authentication-problems", - "redirect_document_id": true - }, + "redirect_url": "/azure/bot-service/bot-service-troubleshoot-authentication-problems", + "redirect_document_id": true + }, { "source_path": "articles/azure/azure-bot-service-quickstart.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-quickstart", + "redirect_document_id": false }, { "source_path": "articles/azure/azure-bot-service-serverless-template-basic.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-debug-bot", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-debug-bot", + "redirect_document_id": false }, { "source_path": "articles/troubleshoot-general-problems.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-troubleshoot-general-problems", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-troubleshoot-general-problems", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-anatomy.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-basics", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-create-middleware.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-concept-middleware", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-concept-middleware", + "redirect_document_id": true }, { "source_path": "articles/bot-builder-deploy-samples.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_document_id": false }, { "source_path": "articles/bot-builder-tools.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_document_id": false }, { "source_path": "articles/bot-service-activity-spec.md", "redirect_url": "https://github.com/Microsoft/BotBuilder/tree/master/specs/botframework-activity", - "redirect_document_id": true + "redirect_document_id": false }, { "source_path": "articles/bot-service-build-download-source-code.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-deploy-az-cli", + "redirect_document_id": false }, { "source_path": "articles/bot-service-channel-inspector.md", "redirect_url": "https://github.com/microsoft/botbuilder-samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/bot-service-continuous-deployment.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-build-continuous-deployment", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-build-continuous-deployment", + "redirect_document_id": true }, { "source_path": "articles/bot-service-manage-test-webchat.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-quickstart", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-quickstart", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-core-authentication.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-authentication", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-tutorial-authentication", + "redirect_document_id": true + }, + { + "source_path": "articles/dotnet/bot-builder-dotnet-sdk-quickstart.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-quickstart.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-sdk-quickstart", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/python/bot-builder-python-quickstart.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/javascript/bot-builder-javascript-quickstart.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/java/bot-builder-java-quickstart.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-tutorial-create-basic-bot.md", + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-real-time-audio-video-call-overview.md", - "redirect_url": "https://aka.ms/realTimeMediaCalling-repo", - "redirect_document_id": false + "redirect_url": "https://github.com/Microsoft/BotBuilder-RealTimeMediaCalling", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-real-time-deploy-visual-studio.md", - "redirect_url": "https://aka.ms/realTimeMediaCalling-repo", - "redirect_document_id": false + "redirect_url": "https://github.com/Microsoft/BotBuilder-RealTimeMediaCalling", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-real-time-media-concepts.md", - "redirect_url": "https://aka.ms/realTimeMediaCalling-repo", - "redirect_document_id": false + "redirect_url": "https://github.com/Microsoft/BotBuilder-RealTimeMediaCalling", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-real-time-media-requirements.md", - "redirect_url": "https://aka.ms/realTimeMediaCalling-repo", - "redirect_document_id": false + "redirect_url": "https://github.com/Microsoft/BotBuilder-RealTimeMediaCalling", + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-release-notes.md", "redirect_url": "https://github.com/Microsoft/BotBuilder/releases", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/dotnet/bot-builder-dotnet-samples.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/dotnet/index.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-overview", - "redirect_document_id": false - }, - { - "source_path": "articles/java/bot-builder-java-quickstart.md", - "redirect_url": "https://github.com/Microsoft/botbuilder-java/wiki", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/dotnet/bot-builder-dotnet-overview", + "redirect_document_id": false }, { "source_path": "articles/nodejs/bot-builder-nodejs-dialog-manage-conversation.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", + "redirect_document_id": true }, { "source_path": "articles/nodejs/bot-builder-nodejs-global-handlers.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-dialog-actions", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-dialog-actions", + "redirect_document_id": false }, { "source_path": "articles/nodejs/bot-builder-nodejs-manage-complex-conversation.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", + "redirect_document_id": false }, { "source_path": "articles/nodejs/bot-builder-nodejs-manage-conversation-flow.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow", + "redirect_document_id": false }, { "source_path": "articles/nodejs/bot-builder-nodejs-prompts.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-dialog-prompt", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-dialog-prompt", + "redirect_document_id": true }, { "source_path": "articles/nodejs/bot-builder-nodejs-quickstart.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/javascript/bot-builder-javascript-quickstart", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-service-quickstart-create-bot", + "redirect_document_id": true }, { "source_path": "articles/nodejs/bot-builder-nodejs-recognize-intent.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-messages", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-recognize-intent-messages", + "redirect_document_id": true }, { "source_path": "articles/nodejs/bot-builder-nodejs-samples.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/nodejs/bot-builder-nodejs-save-user-data.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-state", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-state", + "redirect_document_id": true }, { "source_path": "articles/nodejs/bot-builder-nodejs-use-default-message-handler.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-message-create", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-message-create", + "redirect_document_id": true }, { "source_path": "articles/nodejs/index.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/nodejs/bot-builder-nodejs-overview", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/nodejs/bot-builder-nodejs-overview", + "redirect_document_id": true }, { "source_path": "articles/rest-api/bot-framework-rest-direct-line-concepts.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts", + "redirect_document_id": true }, { "source_path": "articles/rest-api/index.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-overview", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/rest-api/bot-framework-rest-overview", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-dialog-state.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-concept-state", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-concept-state", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-how-to-translation.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-cosmos-middleware.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-storage", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-howto-v4-storage", + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-classic.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-direct-line.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-event-handlers.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-concept-middleware", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-concept-middleware", + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-translation.md", "redirect_url": "https://github.com/Microsoft/BotBuilder-Samples", - "redirect_document_id": false + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-v4-luisgen.md", "redirect_url": "https://github.com/Microsoft/botbuilder-tools/blob/master/packages/LUISGen/src/npm/readme.md", - "redirect_document_id": true + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-prompts.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-dialog-manage-conversation-flow", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-dialog-manage-conversation-flow", + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-proactive-messages.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-proactive-message", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-howto-proactive-message", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-storage-concept.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-storage", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-howto-v4-storage", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-welcome-user.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-send-welcome-message", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-send-welcome-message", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-concepts.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false }, { "source_path": "articles/bot-service-build-online-code-editor.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-basics", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false }, { "source_path": "articles/bot-service-change-hosting-plan.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-basics", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-basics", + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-howto-add-input-hints.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-add-suggested-actions", - "redirect_document_id": false + "redirect_url": "/azure/bot-service/bot-builder-howto-add-suggested-actions", + "redirect_document_id": false }, { "source_path": "articles/v4sdk/bot-builder-enterprise-template-overview.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-virtual-assistant-introduction", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-virtual-assistant-introduction", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-enterprise-template-getting-started.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-skills-overview", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-skills-overview", + "redirect_document_id": true }, { "source_path": "articles/v4sdk/bot-builder-enterprise-template-customize.md", - "redirect_url": "https://docs.microsoft.com/azure/bot-service/bot-builder-virtual-assistant-template", - "redirect_document_id": true + "redirect_url": "/azure/bot-service/bot-builder-virtual-assistant-template", + "redirect_document_id": true + }, + { + "source_path": "rest/index.md", + "redirect_url": "api/conversations", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-dialogs-greeting.md", + "redirect_url": "/azure/bot-service/bot-builder-send-welcome-message", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-tutorial-basic-deploy.md", + "redirect_url": "/azure/bot-service/bot-builder-tutorial-create-basic-bot", + "redirect_document_id": true + }, + { + "source_path": "articles/v4sdk/bot-builder-telemetry-reference.md", + "redirect_url": "/azure/bot-service/bot-builder-telemetry", + "redirect_document_id": true + }, + { + "source_path": "articles/v4sdk/bot-builder-virtual-assistant-template.md", + "redirect_url": "/azure/bot-service/bot-builder-virtual-assistant-introduction", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/bot-builder-skills-overview.md", + "redirect_url": "/azure/bot-service/bot-builder-virtual-assistant-introduction", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-manage-speech-priming.md", + "redirect_url": "/azure/bot-service/", + "redirect_document_id": false + }, + { + "source_path": "articles/v4sdk/skills-write-manifest-2-1.md", + "redirect_url": "/azure/bot-service/skills-write-manifest", + "redirect_document_id": true + }, + { + "source_path": "articles/v4sdk/skills-write-manifest-2-0.md", + "redirect_url": "/azure/bot-service/skills-write-manifest", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-channel-connect-cortana.md", + "redirect_url": "/azure/bot-service/bot-service-manage-channels", + "redirect_document_id": false + }, + { + "source_path": "articles/what-is-new-archive.md", + "redirect_url": "/azure/bot-service/what-is-new", + "redirect_document_id": true + }, + { + "source_path": "articles/bot-service-overview-introduction.md", + "redirect_url": "/azure/bot-service/bot-service-overview", + "redirect_document_id": true + }, + { + "source_path": "articles/bot-service-debug-channel-ngrok.md", + "redirect_url": "articles/bot-service-debug-channel-devtunnel", + "redirect_document_id": false + }, + { + "source_path": "articles/bot-service-channel-connect-directlinespeech.md", + "redirect_url": "articles/bot-service-channel-directline", + "redirect_document_id": false + }, + { + "source_path": "articles/directline-speech-bot.md", + "redirect_url": "articles/bot-service-channel-directline", + "redirect_document_id": false } ] } diff --git a/LICENSE b/LICENSE index 57b3c5b98..44e250339 100644 --- a/LICENSE +++ b/LICENSE @@ -379,7 +379,7 @@ Section 8 -- Interpretation. Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons +will be considered the "Licensor." The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as diff --git a/README.md b/README.md index 37bf1cf49..5a796a814 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. # Bot Framework Technical Documentation Contributor Guide -You've found the GitHub repository that houses the source for the Bot Framework technical documentation that is published on [https://docs.microsoft.com/bot-framework](https://docs.microsoft.com/bot-framework). +You've found the GitHub repository that houses the source for the Bot Framework technical documentation that is published at [Bot Framework documentation](https://learn.microsoft.com/bot-framework). This repository also contains guidance to help you contribute to our technical documentation. For a list of the articles in the contributors' guide, see [the index](contributor-guide/contributor-guide-index.md). ## Contribute to Bot Framework documentation + Thank you for your interest in Bot Framework documentation! * [Ways to contribute](#ways-to-contribute) @@ -21,20 +22,21 @@ Thank you for your interest in Bot Framework documentation! * [Index of all contributors' guide articles](contributor-guide/contributor-guide-index.md) (opens new page) ## Ways to contribute -You can submit updates to the [Bot Framework documentation](https://docs.microsoft.com/bot-framework) as follows: +You can submit updates to the [Bot Framework documentation](https://learn.microsoft.com/bot-framework) as follows: -* You can easily contribute to technical articles in the GitHub user interface. Either find the article in this repository, or visit the article on [https://docs.microsoft.com/bot-framework](https://docs.microsoft.com/bot-framework) and click the link in the article that goes to the GitHub source for the article. +* You can easily contribute to technical articles in the GitHub user interface. Either find the article in this repository, or visit the article at [Bot Framework documentation](https://learn.microsoft.com/bot-framework) and click the link in the article that goes to the GitHub source for the article. * If you are making substantial changes to an existing article, adding or changing images, or contributing a new article, you need to fork this repository, install Git Bash, Markdown Pad, and learn some git commands. ## About your contributions to Bot Framework content ### Minor corrections -Minor corrections or clarifications you submit for documentation and code examples in this repo are covered by the [docs.microsoft.com Terms of Use](https://docs.microsoft.com/legal/termsofuse). + +Minor corrections or clarifications you submit for documentation and code examples in this repo are covered by the [learn.microsoft.com Terms of Use](https://learn.microsoft.com/legal/termsofuse). ### Larger submissions If you submit a pull request with new or significant changes to documentation and code examples, we'll send a comment in GitHub asking you to submit an online Contribution License Agreement (CLA) if you are not an employee of Microsoft. We need you to complete the online form before we can accept your pull request. ## Repository organization -The content in the Bot Framework-docs repository follows the organization of documentation on https://docs.microsoft.com/bot-framework. This repository contains two root folders: +The content in the Bot Framework-docs repository follows the organization of the [Bot Framework documentation](https://learn.microsoft.com/bot-framework). This repository contains two root folders: ### \articles The *\articles* folder contains the documentation articles formatted as markdown files with an *.md* extension. Articles are typically grouped by Bot Framework service. @@ -84,4 +86,5 @@ Automated labels are assigned to pull requests to help us manage the pull reques * Change sent to author: The author has been notified of the pending pull request. ## More resources -See the [index of our contributor's guide](contributor-guide/contributor-guide-index.md) for all our guidance topics. + +See the [index of our contributor guide](contributor-guide/contributor-guide-index.md) for all our guidance topics. diff --git a/art-source/nomnoml/01.multi-turn-prompt-adaptive.md b/art-source/nomnoml/01.multi-turn-prompt-adaptive.md new file mode 100644 index 000000000..bd81bd138 --- /dev/null +++ b/art-source/nomnoml/01.multi-turn-prompt-adaptive.md @@ -0,0 +1,43 @@ + +Diagram for the adaptive dialog sample 01.multi-turn-prompt, used in +HowTo \ Develop \ **Create a bot using adaptive dialogs**. + +## Csharp + +```nomnoml + +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 26 +#fill: #def; #acf +#ranker: network-simplex + +// Based on NuGet package dependencies + +[ RootDialog | +[ OnConversationUpdateActivity | +[ Foreach | +[IfCondition-user] yes -> [SendActivity-welcome] +] +] +[ OnBeginDialog | +[TextInput-ModeOfTransportation]->[TextInput-name] +[TextInput-name]->[SendActivity-acknowldege] +[SendActivity-acknowldege]->[ConfirmInput-give-age] +[ConfirmInput-give-age]->[IfCondition-give-age] +[IfCondition-give-age] yes -> [NumberInput-age] +[NumberInput-age] -> [SendActivity-echo-age] +[IfCondition-give-age] no -> [SendActivity-no-age] +[SendActivity-echo-age] -> [ConfirmInput-all-good] +[SendActivity-no-age] -> [ConfirmInput-all-good] +[ConfirmInput-all-good]->[SendActivity-summary] +[SendActivity-summary]->[EndDialog] +] +] + +``` diff --git a/art-source/nomnoml/03.welcome-users.md b/art-source/nomnoml/03.welcome-users.md index 6140ec4f2..77cba6cc1 100644 --- a/art-source/nomnoml/03.welcome-users.md +++ b/art-source/nomnoml/03.welcome-users.md @@ -57,4 +57,4 @@ Flow Diagram for sample 03.welcome-users, used in HowTo \ Develop \ **Send welco [send_activity()]--[first_message] ] -``` \ No newline at end of file +``` diff --git a/art-source/nomnoml/12.custom-qa.md b/art-source/nomnoml/12.custom-qa.md new file mode 100644 index 000000000..2cb00f54c --- /dev/null +++ b/art-source/nomnoml/12.custom-qa.md @@ -0,0 +1,63 @@ +Flow Diagram for sample 12.CustomQABot, used in HowTo \ Develop \ **Use question answering to answer questions**. + +## C# + +```nomnoml + +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf + + +[CustomQABot| + [OnMessageActivityAsync] + [customQuestionAnswering| + [GetAnswersAsync] + ] + [OnMessageActivityAsync]->[customQuestionAnswering] +] + +[Language Studio| + knowledgebase +] + +[CustomQABot]->[Language Studio] + +``` + +## JavaScript + +```nomnoml + +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf + + +[CustomQABot| + [onMessage] + [CustomQuestionAnswering| + [getAnswers] + ] + [onMessage]->[CustomQuestionAnswering] +] + +[Language Studio| + knowledgebase +] + +[CustomQABot]->[Language Studio] + +``` diff --git a/art-source/nomnoml/13.core-bot.md b/art-source/nomnoml/13.core-bot.md index e854d0d8b..2b95fce89 100644 --- a/art-source/nomnoml/13.core-bot.md +++ b/art-source/nomnoml/13.core-bot.md @@ -92,4 +92,4 @@ Diagrams for sample 13.core-bot, used in **Add natural language understanding to [BookingDialog]->[BookingDetails] [DialogBot]->[DialogHelper] -``` \ No newline at end of file +``` diff --git a/art-source/nomnoml/14.nlp-with-dispatch.md b/art-source/nomnoml/14.nlp-with-dispatch.md index 51ab9bf0e..d33d2ff27 100644 --- a/art-source/nomnoml/14.nlp-with-dispatch.md +++ b/art-source/nomnoml/14.nlp-with-dispatch.md @@ -41,4 +41,4 @@ Flow Diagram for sample 14.nlp-with-dispatch, used in HowTo \ Develop \ **Use mu [_dispatch_to_top_intent]->[_process_weather] [_dispatch_to_top_intent]->[_process_sample_qna] ] -``` \ No newline at end of file +``` diff --git a/art-source/nomnoml/18.bot-authentication.md b/art-source/nomnoml/18.bot-authentication.md index 755c86b45..421f4fd5f 100644 --- a/art-source/nomnoml/18.bot-authentication.md +++ b/art-source/nomnoml/18.bot-authentication.md @@ -1,4 +1,4 @@ -Diagrams for sample 18.bot-authentication, used in **Add authentication to your bot via Azure Bot Service** how-to article. +Diagrams for sample 18.bot-authentication, used in **Add authentication to your bot via Azure AI Bot Service** how-to article. ## JavaScript @@ -59,4 +59,4 @@ Diagrams for sample 18.bot-authentication, used in **Add authentication to your [AuthBot]->[MainDialog] [MainDialog]-:>[LogoutDialog] -``` \ No newline at end of file +``` diff --git a/art-source/nomnoml/43.complex-dialog.md b/art-source/nomnoml/43.complex-dialog.md index 85619cdee..4d2cd63c6 100644 --- a/art-source/nomnoml/43.complex-dialog.md +++ b/art-source/nomnoml/43.complex-dialog.md @@ -138,4 +138,4 @@ Diagrams for sample 43.complex-dialog, used in *Create advanced conversation flo [DialogBot]->[MainDialog] [MainDialog]->[TopLevelDialog] [TopLevelDialog]->[ReviewSelectionDialog] -``` \ No newline at end of file +``` diff --git a/art-source/nomnoml/80.skills-simple-bot-to-bot.md b/art-source/nomnoml/80.skills-simple-bot-to-bot.md index fbe63283c..992e77ed1 100644 --- a/art-source/nomnoml/80.skills-simple-bot-to-bot.md +++ b/art-source/nomnoml/80.skills-simple-bot-to-bot.md @@ -55,40 +55,6 @@ Flow diagrams for sample 80.skills-simple-bot-to-bot, used for articles in the H ## For [Implement a skill consumer](/articles/v4sdk/skill-implement-consumer.md) ### Python -```nomnoml -#font: Segoe UI -#fontSize: 9 -#lineWidth: 1 -#arrowSize: 1 -#bendSize:0.3 -#edges: rounded -#padding: 8 -#spacing: 16 -#fill: #def; #acf -#direction: right -#.package: direction=down - -[ RootBot | -[SkillHandler] -> [ADAPTER] - [ADAPTER]-> [AuthenticationConfiguration] - [AuthenticationConfiguration] -> [AllowedSkillsClaimsValidator] -[SkillHandler] -> [BOT] - [BOT] -> [SkillConfiguration] - [BOT] -> [SkillHttpClient] - - -[SkillHttpClient]->[SkillConversationIdFactory] -[SkillHttpClient]->[SimpleCredentialProvider] - -[SkillHandler] -> [SkillConversationIdFactory] -[SkillHandler] -> [SimpleCredentialProvider] -[SkillHandler] -> [AuthenticationConfiguration] -] - -[RootBot]-[EchoSkillBot] -``` - -### Python - take 2 ```nomnoml #font: Segoe UI @@ -106,7 +72,6 @@ Flow diagrams for sample 80.skills-simple-bot-to-bot, used for articles in the H [ simple-root-bot | [SkillConfiguration]<--[BOT] [BOT]-[ADAPTER] - [ADAPTER]+->[AuthenticationConfiguration] [SKILL_HANDLER]+->[AuthenticationConfiguration] [AuthenticationConfiguration]-->[AllowedSkillsClaimsValidator] @@ -140,7 +105,6 @@ Flow diagrams for sample 80.skills-simple-bot-to-bot, used for articles in the H [ SimpleRootBot | [botFrameworkSkill]<--[bot] [bot]-[adapter] - [adapter]+->[AuthenticationConfiguration] [handler]+->[AuthenticationConfiguration] [AuthenticationConfiguration]-->[allowedSkillsClaimsValidator] @@ -155,34 +119,3 @@ Flow diagrams for sample 80.skills-simple-bot-to-bot, used for articles in the H [SimpleRootBot]-[EchoSkillBot] ``` - -## For [Add claims validation](/articles/v4sdk/skill-add-claims-validation.md) - -### JavaScript - -```nomnoml -#font: Segoe UI -#fontSize: 9 -#lineWidth: 1 -#arrowSize: 1 -#bendSize:0.3 -#edges: rounded -#padding: 8 -#spacing: 16 -#fill: #def; #acf -#direction: right -#.package: direction=down - -[ SimpleRootBot | - [rootBot]-[adapterWithErrorHandler] - [adapterWithErrorHandler]-->[authenticationConfiguration] - [skillHandler]-->[authenticationConfiguration] -] - -[ EchoSkillBot | - [echoBot]-[skillAdapterWithErrorHandler] - [skillAdapterWithErrorHandler]-->[authenticationConfiguration] -] - -[SimpleRootBot]-[EchoSkillBot] -``` diff --git a/art-source/nomnoml/81.skills-skilldialog.md b/art-source/nomnoml/81.skills-skilldialog.md new file mode 100644 index 000000000..e8aabef9a --- /dev/null +++ b/art-source/nomnoml/81.skills-skilldialog.md @@ -0,0 +1,141 @@ +Flow diagrams for sample 81.skills-skilldialog, used for articles in the HowTo \ Develop \ Skills section. + +## For [Use a dialog to consume a skill](/articles/v4sdk/skilldialog-howto.md) + +### JavaScript + +```nomnoml +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf +#direction: right +#.package: direction=down + +[ dialogRootBot | + [authConfig]+->[allowedSkillsClaimsValidator] + [handler]->[authConfig] + [handler]->[bot] + + [adapter]<-[handler] + [bot]->[mainDialog] + [bot]<--[adapter] + [mainDialog]+->[SkillDialog] + [SkillDialog]->[skillClient] + [mainDialog]->[skillClient] + [adapter]->[skillClient] + [SkillDialog]-->[bot] + [handler]->[conversationIdFactory] + [mainDialog]->[conversationIdFactory] + [SkillDialog]->[conversationIdFactory] + [skillClient]->[conversationIdFactory] +] + +[dialogRootBot]-[ dialogSkillBot] +``` + +### Python + +```nomnoml +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf +#direction: right +#.package: direction=down + +[ dialogRootBot | + [AUTH_CONFIG]+->[claims_validator] + [ADAPTER]<-[SKILL_HANDLER] + [SKILL_HANDLER]->[AUTH_CONFIG] + [SKILL_HANDLER]->[BOT] + + [BOT]->[DIALOG] + [BOT]<--[ADAPTER] + [DIALOG]+->[SkillDialog] + [SkillDialog]->[CLIENT] + [DIALOG]->[CLIENT] + [ADAPTER]->[CLIENT] + [SkillDialog]-->[BOT] + [SKILL_HANDLER]->[ID_FACTORY] + [DIALOG]->[ID_FACTORY] + [SkillDialog]->[ID_FACTORY] + [CLIENT]->[ID_FACTORY] +] + +[dialogRootBot]-[ dialogSkillBot] +``` + +## For [Use dialogs within a skill](/articles/v4sdk/skill-actions-in-dialogs.md) + +### JavaScript + +```nomnoml +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf +#direction: right +#.package: direction=down + +[ dialogSkillBot | + [adapter]->[authConfig] + [adapter]->[bot] + [authConfig]->[allowedSkillsClaimsValidator] + [bot]->[activityRouterDialog] + [activityRouterDialog]->[luisRecognizer] + [activityRouterDialog]+->[BookingDialog] + [CancelAndHelpDialog]<:-[BookingDialog] + [BookingDialog]+->[DateResolverDialog] + [CancelAndHelpDialog]<:-[DateResolverDialog] +] + +[dialogRootBot]-[dialogSkillBot] +``` + +### Python + +```nomnoml +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf +#direction: right +#.package: direction=down + +[ dialog-skill-bot | + [ADAPTER]->[AuthenticationConfiguration] + [ADAPTER]->[BOT] + [AuthenticationConfiguration]->[VALIDATOR] + [BOT]->[ROUTER] + [ROUTER]->[RECOGNIZER] + [ROUTER]+->[BookingDialog] + [CancelAndHelpDialog]<:-[BookingDialog] + [BookingDialog]+->[DateResolverDialog] + [CancelAndHelpDialog]<:-[DateResolverDialog] +] + +[dialog-root-bot]-[dialog-skill-bot] +``` + +--- diff --git a/art-source/nomnoml/mixed-dialogs-sample.md b/art-source/nomnoml/mixed-dialogs-sample.md new file mode 100644 index 000000000..41360ab34 --- /dev/null +++ b/art-source/nomnoml/mixed-dialogs-sample.md @@ -0,0 +1,39 @@ +## For [Create a bot using adaptive, component, waterfall, and custom dialogs](/articles/v4sdk/bot-builder-mixed-dialogs.md) + +```nomnoml +#font: Segoe UI +#fontSize: 9 +#lineWidth: 1 +#arrowSize: 1 +#bendSize:0.3 +#edges: rounded +#padding: 8 +#spacing: 16 +#fill: #def; #acf +#ranker: longest-path + +[ <> root | + [waterfall]->[<> fullname] + [waterfall]->[AdaptiveDialog] + [AdaptiveDialog]->[<> address] + [ <> fullname | + first + last + ] + [AdaptiveDialog | + age + shoe size + fullname + address | + [ OnBeginDialog | + [ NumberInput userage]->[ NumberInput shoesize] + [NumberInput shoesize]->[ BeginDialog address] + ] + ] + [ <> address | + street + city + zip + ] +] +``` diff --git a/art-source/power-point/architecture.pptx b/art-source/power-point/architecture.pptx new file mode 100644 index 000000000..1639a906c Binary files /dev/null and b/art-source/power-point/architecture.pptx differ diff --git a/art-source/power-point/bot-concepts-art.pptx b/art-source/power-point/bot-concepts-art.pptx index 6a90602fa..fabf9730b 100644 Binary files a/art-source/power-point/bot-concepts-art.pptx and b/art-source/power-point/bot-concepts-art.pptx differ diff --git a/art-source/power-point/bot-skills-art.pptx b/art-source/power-point/bot-skills-art.pptx index 05a39f589..e79b1c00a 100644 Binary files a/art-source/power-point/bot-skills-art.pptx and b/art-source/power-point/bot-skills-art.pptx differ diff --git a/art-source/power-point/security-authentication.pptx b/art-source/power-point/security-authentication.pptx new file mode 100644 index 000000000..dda493228 Binary files /dev/null and b/art-source/power-point/security-authentication.pptx differ diff --git a/articles/TOC.md b/articles/TOC.md deleted file mode 100644 index 8dbfea1e8..000000000 --- a/articles/TOC.md +++ /dev/null @@ -1,89 +0,0 @@ -# [Azure Bot Service Documentation](index.yml) -# Overview -## [About Azure Bot Service](bot-service-overview-introduction.md) -## [What's new](what-is-new.md) -# Quickstart -## [Create a bot using .NET](dotnet/bot-builder-dotnet-sdk-quickstart.md) -## [Create a bot using JavaScript](javascript/bot-builder-javascript-quickstart.md) -## [Create a bot using Python](python/bot-builder-python-quickstart.md) -## [Create a bot using Azure Bot Service](v4sdk/abs-quickstart.md) -# Tutorials -## [1. Create and deploy a basic bot](v4sdk/bot-builder-tutorial-basic-deploy.md) -## [2. Add QnA Maker and redeploy a bot](v4sdk/bot-builder-tutorial-add-qna.md) -## [Add authentication to your bot](bot-builder-tutorial-authentication.md) -# Samples -## [Bot Framework samples repo on GitHub](https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md) -# Concepts -## [How bots work](v4sdk/bot-builder-basics.md) -## [Managing state](v4sdk/bot-builder-concept-state.md) -## [Dialogs library](v4sdk/bot-builder-concept-dialog.md) -## [Middleware](v4sdk/bot-builder-concept-middleware.md) -## [User authentication](v4sdk/bot-builder-concept-authentication.md) -## [Manage bot resources](v4sdk/bot-file-basics.md) -## [How bots for Microsoft Teams work](v4sdk/bot-builder-basics-teams.md) -## [About skills](v4sdk/skills-conceptual.md) - -## [Bot Service templates](bot-service-concept-templates.md) -## [Cognitive Services](bot-service-concept-intelligence.md) -## [Key scenarios for bots](bot-service-scenario-overview.md) -### [Commerce bot](bot-service-scenario-commerce.md) -### [Cortana Skill bot](bot-service-scenario-cortana-skill.md) -### [Enterprise Productivity bot](bot-service-scenario-enterprise-productivity.md) -### [Information bot](bot-service-scenario-informational.md) -### [Internet of Things bot](bot-service-scenario-internet-things.md) -# How-To -## [Design](design/TOC.md) -## Develop - -### [Send and receive text message](v4sdk/bot-builder-howto-send-messages.md) -### [Add media to messages](v4sdk/bot-builder-howto-add-media-attachments.md) -### [Add buttons to guide user action](v4sdk/bot-builder-howto-add-suggested-actions.md) -### [Save user and conversation data](v4sdk/bot-builder-howto-v4-state.md) -### [Prompt users for input](v4sdk/bot-builder-primitive-prompts.md) -### [Send welcome message to users](v4sdk/bot-builder-send-welcome-message.md) -### [Send proactive notifications to users](v4sdk/bot-builder-howto-proactive-message.md) -### [Implement sequential conversation flow](v4sdk/bot-builder-dialog-manage-conversation-flow.md) -### [Add natural language understanding to your bot](v4sdk/bot-builder-howto-v4-luis.md) -### [Answer user's questions using QnA Maker](v4sdk/bot-builder-howto-qna.md) -### [Use multiple LUIS and QnA models](v4sdk/bot-builder-tutorial-dispatch.md) -### [Create advanced conversation flow using branches and loops](v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md) -### [Reuse dialogs](v4sdk/bot-builder-compositcontrol.md) - -### [Handle user interruptions](v4sdk/bot-builder-howto-handle-user-interrupt.md) -### [Write directly to storage](v4sdk/bot-builder-howto-v4-storage.md) -### [Add authentication to your bot](v4sdk/bot-builder-authentication.md) -### [Implement custom storage for your bot](v4sdk/bot-builder-custom-storage.md) -### Skills -#### [Implement a skill](v4sdk/skill-implement-skill.md) -#### [Implement a skill consumer](v4sdk/skill-implement-consumer.md) - -### [Add telemetry to your bot](v4sdk/bot-builder-telemetry.md) -### [Add telemetry to your QnA bot](v4sdk/bot-builder-telemetry-QnAMaker.md) -### [Analyze your bot's telemetry data](v4sdk/bot-builder-telemetry-analytics-queries.md) -### [Use Direct Line Speech in your bot](directline-speech-bot.md) -### [.NET](dotnet/TOC.md) -### [Node.js](nodejs/TOC.md) -## Test -### [Unit testing bots](v4sdk/unit-test-bots.md) -### [Add trace activities to your bot](v4sdk/using-trace-activities.md) -## [Debug](debug/TOC.md) -## Deploy -### [Deploy your bot to Azure](bot-builder-deploy-az-cli.md) -### [Set up continuous deployment](bot-service-build-continuous-deployment.md) -## [Manage](manage/TOC.md) -## [Migrate](v4sdk/migration/TOC.md) -# Reference -## [.NET SDK v4](https://aka.ms/botframework-v4-cs-sdk) -## [JavaScript SDK v4](https://aka.ms/bot-jssdk-v4) -## [Python SDK v4](https://aka.ms/botframework-v4-python-sdk) -## [REST](rest-api/TOC.md) -## [.NET SDK v3](https://aka.ms/botframework-v3-cs-sdk) -## [Node.js SDK v3](https://aka.ms/bot-jssdk-v3) -## BF CLI tool reference -### [BF CLI overview](v4sdk/bf-cli-overview.md) -### [BF CLI reference](v4sdk/bf-cli-reference.md) -## [Entities and activity types](bot-service-activities-entities.md) -# [Resources](resources/TOC.md) diff --git a/articles/TOC.yml b/articles/TOC.yml new file mode 100644 index 000000000..0d9ac57e3 --- /dev/null +++ b/articles/TOC.yml @@ -0,0 +1,489 @@ +- name: Bot Framework SDK documentation + href: index-bf-sdk.yml +- name: Overview + items: + - name: What is the Bot Framework SDK? + href: bot-service-overview.md + - name: Choose the right chatbot solution + href: bot-overview.md + - name: What's new + href: what-is-new.md +- name: Quickstart + items: + - name: Create a basic bot + href: bot-service-quickstart-create-bot.md + - name: Create an Azure Bot resource + href: v4sdk/abs-quickstart.md +- name: Samples + items: + - name: Bot Framework samples repo on GitHub + href: https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md +- name: Security + items: + - name: Security baseline + href: /security/benchmark/azure/baselines/bot-service-security-baseline?toc=/azure/bot-service/toc.json + - name: Bot Service encryption + href: bot-service-encryption.md + - name: Authentication basics + href: v4sdk/bot-builder-authentication-basics.md + - name: Authentication types + href: v4sdk/bot-builder-concept-authentication-types.md + - name: User authentication + items: + - name: User authentication basics + href: v4sdk/bot-builder-concept-authentication.md + - name: Identity providers + href: v4sdk/bot-builder-concept-identity-providers.md + - name: Identity providers proxy + href: v4sdk/bot-builder-concept-identity-providers-proxy.md + - name: Single sign-on + href: v4sdk/bot-builder-concept-sso.md + - name: Add authentication to a bot + href: v4sdk/bot-builder-authentication.md + - name: Add single sign-on to a bot + href: v4sdk/bot-builder-authentication-sso.md + - name: Add authentication using federated credentials + href: v4sdk/bot-builder-authentication-federated-credential.md + - name: Enhanced authentication + href: v4sdk/bot-builder-security-enhanced.md + - name: Security guidelines + href: v4sdk/bot-builder-security-guidelines.md +- name: Concepts + items: + - name: Bot + items: + - name: How bots work + href: v4sdk/bot-builder-basics.md + - name: Managing state + href: v4sdk/bot-builder-concept-state.md + - name: Event-driven conversations + href: v4sdk/bot-activity-handler-concept.md + - name: Dialogs library + href: v4sdk/bot-builder-concept-dialog.md + - name: Waterfall dialogs + href: v4sdk/bot-builder-concept-waterfall-dialogs.md + - name: Middleware + href: v4sdk/bot-builder-concept-middleware.md + - name: Natural language understanding + href: v4sdk/bot-builder-concept-luis.md + - name: Understand the structure of an echo bot + href: v4sdk/bot-builder-create-a-bot-project.md + - name: How bots for Microsoft Teams work + href: v4sdk/bot-builder-basics-teams.md + - name: Regionalization in Azure AI Bot Service + href: v4sdk/bot-builder-concept-regionalization.md + - name: Monitor Bot Service + href: monitor-bot-service.md + - name: Skills + items: + - name: About skills + href: v4sdk/skills-conceptual.md + - name: About skill consumers + href: v4sdk/skills-about-skill-consumers.md + - name: Adaptive expressions + href: v4sdk/bot-builder-concept-adaptive-expressions.md + - name: Language Generation + href: v4sdk/bot-builder-concept-language-generation.md +- name: How-To + items: + - name: Design + items: + - name: Principles of bot design + href: bot-service-design-principles.md + - name: First interaction + href: bot-service-design-first-interaction.md + - name: Design bot elements + items: + - name: Design and control conversation flow + href: bot-service-design-conversation-flow.md + - name: Design bot navigation + href: bot-service-design-navigation.md + - name: Design the user experience + href: bot-service-design-user-experience.md + - name: Patterns + items: + - name: Knowledge base + href: bot-service-design-pattern-knowledge-base.md + - name: Handoff to human + href: bot-service-design-pattern-handoff-human.md + - name: Bots in apps + href: bot-service-design-pattern-embed-app.md + - name: Bots in websites + href: bot-service-design-pattern-embed-web-site.md + - name: Develop + items: + - name: Send and receive text messages + href: v4sdk/bot-builder-howto-send-messages.md + - name: Add media to messages + href: v4sdk/bot-builder-howto-add-media-attachments.md + - name: Add buttons to guide user action + href: v4sdk/bot-builder-howto-add-suggested-actions.md + - name: Save user and conversation data + href: v4sdk/bot-builder-howto-v4-state.md + - name: Prompt users for input + href: v4sdk/bot-builder-primitive-prompts.md + - name: Send welcome message to users + href: v4sdk/bot-builder-send-welcome-message.md + - name: Send proactive notifications to users + href: v4sdk/bot-builder-howto-proactive-message.md + - name: Manage a long-running operation + href: v4sdk/bot-builder-howto-long-operations-guidance.md + - name: Implement sequential conversation flow + href: v4sdk/bot-builder-dialog-manage-conversation-flow.md + - name: Manage dialog complexity + href: v4sdk/bot-builder-compositcontrol.md + - name: Create advanced conversation flow using branches and loops + href: v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md + - name: Handle user interruptions + href: v4sdk/bot-builder-howto-handle-user-interrupt.md + - name: Expire a conversation + href: v4sdk/bot-builder-howto-expire-conversation.md + - name: Language understanding + items: + - name: Add natural language understanding to your bot + href: v4sdk/bot-builder-howto-v4-luis.md + - name: Answer questions with custom question answering + href: bot-builder-howto-answer-questions.md + - name: Answer user's questions using QnA Maker + href: v4sdk/bot-builder-howto-qna.md + - name: Use Orchestrator for intent resolution + href: v4sdk/bot-builder-tutorial-orchestrator.md + - name: Write directly to storage + href: v4sdk/bot-builder-howto-v4-storage.md + - name: Implement custom storage for your bot + href: v4sdk/bot-builder-custom-storage.md + - name: Skills + items: + - name: Implement a skill + href: v4sdk/skill-implement-skill.md + - name: Write a skill manifest + href: v4sdk/skills-write-manifest.md + - name: Use dialogs within a skill + href: v4sdk/skill-actions-in-dialogs.md + - name: Implement a skill consumer + href: v4sdk/skill-implement-consumer.md + - name: Use a dialog to consume a skill + href: v4sdk/skill-use-skilldialog.md + - name: Add telemetry to your bot + href: v4sdk/bot-builder-telemetry.md + - name: Add telemetry to your QnA bot + href: v4sdk/bot-builder-telemetry-QnAMaker.md + - name: Analyze your bot's telemetry data + href: v4sdk/bot-builder-telemetry-analytics-queries.md + - name: Test + items: + - name: Unit testing bots + href: v4sdk/unit-test-bots.md + - name: Add trace activities to your bot + href: v4sdk/using-trace-activities.md + - name: Debug + items: + - name: Debugging guidelines + href: v4sdk/bot-builder-testing-debugging.md + - name: Debug a bot using IDE + href: bot-service-debug-bot.md + - name: Debug with the Bot Framework Emulator + href: bot-service-debug-emulator.md + - name: Debug a bot from any channel using Dev Tunnels + href: bot-service-debug-channel-devtunnel.md + - name: Debug a skill or skill consumer + href: v4sdk/skills-debug-skill-or-consumer.md + - name: Debug a bot with inspection middleware + href: bot-service-debug-inspection-middleware.md + - name: Debug your bot using transcript files + href: v4sdk/bot-builder-debug-transcript.md + - name: Deploy + items: + - name: Deploy your bot to Azure + displayName: Provision and publish your bot to Azure + href: provision-and-publish-a-bot.md + - name: Provision an App Service resource + displayName: Create an App Service for a bot + href: provision-app-service.md + - name: Provision an Azure Bot resource + displayName: Create an Azure Bot resource + href: provision-azure-bot.md + - name: Configure an Azure Government bot + href: how-to-deploy-gov-cloud-high.md + - name: Configure a bot in Microsoft Azure operated by 21Vianet + href: how-to-deploy-china-cloud.md + - name: Set up continuous deployment + displayName: Set up continuous integration and delivery, Set up CI/CD, Set up CICD + href: bot-service-build-continuous-deployment.md + - name: Manage + items: + - name: Manage a bot + href: bot-service-manage-overview.md + - name: Register a bot with Azure + href: bot-service-quickstart-registration.md + - name: Reliability guidance + href: bot-service-reliability-guidance.md + - name: Configure bot settings + displayName: endpoint, icon + href: bot-service-manage-settings.md + - name: Channels + items: + - name: Connect a bot to channels + href: bot-service-manage-channels.md + - name: Channel reference + href: bot-service-channels-reference.md + - name: Implement channel-specific functionality + href: v4sdk/bot-builder-channeldata.md + - name: Direct Line + items: + - name: About Direct Line + href: bot-service-channel-directline.md + - name: Connect to Direct Line + href: bot-service-channel-connect-directline.md + - name: Direct Line App Service extension + items: + - name: Direct Line App Service extension + href: bot-service-channel-directline-extension.md + - name: About network isolation + href: dl-network-isolation-concept.md + - name: Configure .NET bot for extension + href: bot-service-channel-directline-extension-net-bot.md + - name: Configure Node.js bot for extension + href: bot-service-channel-directline-extension-node-bot.md + - name: Create .NET client with extension + href: bot-service-channel-directline-extension-net-client.md + - name: Use extension with Web Chat + href: bot-service-channel-directline-extension-webchat-client.md + - name: Configure a virtual network + displayName: Use Direct Line App Service extension within a VNET, Use Direct Line App Service extension within a virtual network + href: bot-service-channel-directline-extension-vnet.md + - name: Configure network isolation + href: dl-network-isolation-how-to.md + - name: Alexa + href: bot-service-channel-connect-alexa.md + - name: Azure Communication Services + href: bot-service-channel-azure-communication.md + - name: Email + href: bot-service-channel-connect-email.md + - name: Facebook + href: bot-service-channel-connect-facebook.md + - name: GroupMe + href: bot-service-channel-connect-groupme.md + - name: LINE + href: bot-service-channel-connect-line.md + - name: Microsoft Teams + href: channel-connect-teams.md + - name: M365 (preview) + href: bot-service-channel-connect-m365.md + - name: Omnichannel + href: bot-service-channel-omnichannel.md + - name: Outlook (preview) + href: bot-service-channel-connect-actionable-email.md + - name: Search (preview) + href: bot-service-channel-connect-search.md + - name: Skype + href: bot-service-channel-connect-skype.md + - name: Slack + href: bot-service-channel-connect-slack.md + - name: Telegram + href: bot-service-channel-connect-telegram.md + - name: Twilio (SMS) + href: bot-service-channel-connect-twilio.md + - name: WeChat + href: bot-service-channel-connect-wechat.md + - name: Web Chat + href: bot-service-channel-connect-webchat.md + - name: Additional channels + href: bot-service-channel-additional-channels.md + - name: Connect to channels with Azure CLI + href: bot-service-channel-azure-cli.md +- name: Reference + items: + - name: v4 SDK reference + items: + - name: .NET SDK v4 + href: /dotnet/api/?view=botbuilder-dotnet-stable&preserve-view=true + - name: JavaScript SDK v4 + href: /javascript/api/?view=botbuilder-ts-latest&preserve-view=true + - name: Java SDK v4 + href: /java/api/?view=botbuilder-java&preserve-view=true + - name: Python SDK v4 + href: /python/api/?view=botbuilder-py-latest&preserve-view=true + - name: Monitoring data reference + href: monitor-bot-service-reference.md + - name: REST API reference + items: + - name: Bot Framework REST API + items: + - name: Overview + href: rest-api/bot-framework-rest-overview.md + - name: Key concepts + href: rest-api/bot-framework-rest-connector-concepts.md + - name: Create a bot with REST + href: rest-api/bot-framework-rest-connector-quickstart.md + - name: API reference + href: rest-api/bot-framework-rest-connector-api-reference.md + - name: Connector API + items: + - name: Authentication + href: rest-api/bot-framework-rest-connector-authentication.md + - name: Activities overview + href: https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md + - name: Create messages + href: rest-api/bot-framework-rest-connector-create-messages.md + - name: Send and receive messages + href: rest-api/bot-framework-rest-connector-send-and-receive-messages.md + - name: Add media attachments to messages + href: rest-api/bot-framework-rest-connector-add-media-attachments.md + - name: Add rich cards to messages + href: rest-api/bot-framework-rest-connector-add-rich-cards.md + - name: Add speech to messages + href: rest-api/bot-framework-rest-connector-text-to-speech.md + - name: Add input hints to messages + href: rest-api/bot-framework-rest-connector-add-input-hints.md + - name: Add suggested actions to messages + href: rest-api/bot-framework-rest-connector-add-suggested-actions.md + - name: Implement channel-specific functionality + href: rest-api/bot-framework-rest-connector-channeldata.md + - name: Manage state data + href: rest-api/bot-framework-rest-state.md + - name: Direct Line API 3.0 + items: + - name: Key concepts + href: rest-api/bot-framework-rest-direct-line-3-0-concepts.md + - name: Authentication + href: rest-api/bot-framework-rest-direct-line-3-0-authentication.md + - name: Start a conversation + href: rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md + - name: Reconnect to a conversation + href: rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md + - name: Send an activity to the bot + href: rest-api/bot-framework-rest-direct-line-3-0-send-activity.md + - name: Receive activities from the bot + href: rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md + - name: End a conversation + href: rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md + - name: API reference + href: rest-api/bot-framework-rest-direct-line-3-0-api-reference.md + - name: Swagger file + href: https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-protocol/directline-3.0.json + - name: Direct Line API 1.1 + items: + - name: Key concepts + href: rest-api/bot-framework-rest-direct-line-1-1-concepts.md + - name: Authentication + href: rest-api/bot-framework-rest-direct-line-1-1-authentication.md + - name: Start a conversation + href: rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md + - name: Send a message to the bot + href: rest-api/bot-framework-rest-direct-line-1-1-send-message.md + - name: Receive messages from the bot + href: rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md + - name: API reference + href: rest-api/bot-framework-rest-direct-line-1-1-api-reference.md + - name: Swagger file + href: https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-protocol/directline-1.1.json + - name: Bot Framework CLI tool + href: v4sdk/bf-cli-overview.md + - name: Adaptive dialogs + items: + - name: Events and triggers + href: adaptive-dialog/adaptive-dialog-prebuilt-triggers.md + - name: Actions + href: adaptive-dialog/adaptive-dialog-prebuilt-actions.md + - name: Inputs + href: adaptive-dialog/adaptive-dialog-prebuilt-inputs.md + - name: Recognizers + href: adaptive-dialog/adaptive-dialog-prebuilt-recognizers.md + - name: Memory scopes + href: adaptive-dialog/adaptive-dialog-prebuilt-memory-states.md + - name: Adaptive expressions + items: + - name: Prebuilt functions + href: adaptive-expressions/adaptive-expressions-prebuilt-functions.md + - name: API reference + href: adaptive-expressions/adaptive-expressions-API-reference.md + - name: LG + items: + - name: API reference + href: language-generation/language-generation-API-reference.md + - name: Structured response template + href: language-generation/language-generation-structured-response-template.md + - name: Functions injected from LG + href: language-generation/functions-injected-from-language-generation.md + - name: File formats + items: + - name: .lg file format + href: file-format/bot-builder-lg-file-format.md + - name: .lu file format + href: file-format/bot-builder-lu-file-format.md + - name: .qna file format + href: file-format/bot-builder-qna-file-format.md + - name: Entities and activity types + href: bot-service-activities-entities.md + - name: OAuth URL support + href: ref-oauth-redirect-urls.md + - name: Azure Policy built-ins + displayName: samples, policies, definitions + href: policy-reference.md +- name: Resources + items: + - name: Get support + href: bot-service-resources-links-help.md + - name: App Insights keys + href: bot-service-resources-app-insights-keys.md + - name: Bot Service Compliance + href: bot-service-compliance.md + - name: Bot review guidelines + href: bot-service-review-guidelines.md + - name: Guide to identifiers + href: bot-service-resources-identifiers-guide.md + - name: Implement skills for Copilot Studio + href: v4sdk/skill-pva.md + - name: Convert and update skills from multitenant to single-tenant + items: + - name: Convert an existing skill from multitenant to single-tenant + href: v4sdk/skill-pva-convert-skill-single-tenant.md + - name: Update skill to support both single-tenant and multitenant agents + href: v4sdk/skill-pva-update-skill-single-tenant.md + - name: User-agent requests + href: bot-service-resources-user-agent.md + - name: FAQ + items: + - name: Index + href: bot-service-resources-bot-framework-faq.yml + - name: Availability + href: bot-service-resources-faq-availability.yml + - name: General + href: bot-service-resources-faq-general.yml + - name: Ecosystem + href: bot-service-resources-faq-ecosystem.yml + - name: Security + href: bot-service-resources-faq-security.yml + - name: Azure + href: bot-service-resources-faq-azure.yml + - name: Troubleshoot + items: + - name: Index + href: bot-service-troubleshoot-index.md + - name: General + href: bot-service-troubleshoot-general-problems.md + - name: Configuration + href: bot-service-troubleshoot-bot-configuration.md + - name: HTTP 500 errors + href: bot-service-troubleshoot-500-errors.md + - name: Authentication + href: bot-service-troubleshoot-authentication-problems.md + - name: Bot Framework schemas + items: + - name: Activity schema + href: https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md + - name: Card schema + href: https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md + - name: Transcript schema + href: https://github.com/Microsoft/botframework-sdk/blob/main/specs/transcript/transcript.md + - name: Virtual Assistant + href: v4sdk/bot-builder-virtual-assistant-introduction.md + - name: Web Chat + items: + - name: Overview + href: v4sdk/bot-builder-webchat-overview.md + - name: Customization + href: v4sdk/bot-builder-webchat-customization.md + - name: Add single sign-on to Web Chat + href: v4sdk/bot-builder-webchat-sso.md diff --git a/articles/adaptive-dialog/adaptive-dialog-prebuilt-actions.md b/articles/adaptive-dialog/adaptive-dialog-prebuilt-actions.md new file mode 100644 index 000000000..9c9bdc636 --- /dev/null +++ b/articles/adaptive-dialog/adaptive-dialog-prebuilt-actions.md @@ -0,0 +1,109 @@ +--- +title: Prebuilt actions for adaptive dialogs +description: Learn about the adaptive dialog prebuilt actions, grouped by their general purpose. +keywords: bot, actions, adaptive dialogs +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Actions in adaptive dialogs - reference guide + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article lists the actions defined in the Bot Framework SDK, grouped by their general purpose. + +- For an introduction to this topic, see the [Actions](/composer/concept-dialog#action) topic in the Composer documentation. + +## Responses and questions + +| Action Name | Action title | Description | +|:------------------|:-----------------------------------|:----------------------------------------------------------| +| `Ask` | Send a response to ask a question | Uses an activity as a way to prompt the user. | +| `AttachmentInput` | Prompt for a file or an attachment | Asks the user for a file or image. | +| `ChoiceInput` | Prompt with multi-choice | Asks the user to pick from a list of choices. | +| `ConfirmInput` | Prompt for confirmation | Asks the user for confirmation (a yes-no question). | +| `DateTimeInput` | Prompt for a date or a time | Asks the user for a date or time value. | +| `NumberInput` | Prompt for a number | Asks the user for a numeric value. | +| `OAuthInput` | OAuth login | Asks the user to sign in with an OAuth identity provider. | +| `SendActivity` | Send a response | Sends an activity, such as a response to a user. | +| `TextInput` | Prompt for text | Asks the user to type a response. | + +## Conditions and looping + +The conditional actions are designed to help your bot make decisions based on any pre-defined condition that you've created. These actions are specified by a set of conditional statements that have Boolean expressions, which are evaluated to a Boolean value of true or false. + +The remaining actions relate to looping statements which enable you to repeat the execution of a block of code for every element in a collection. + +| Action Name | Action title | Description | +|:------------------|:-------------------------------------|:----------------------------------------------------------------------| +| `BreakLoop` | Break out of loop | Exits the enclosing loop. | +| `ContinueLoop` | Continue loop | Starts the next iteration of the enclosing loop. | +| `ForEach` | Loop: For each item | Runs a set of actions on each item in a collection. | +| `ForEachPage` | Loop: For each page (multiple items) | Runs a set of actions on each page (subset of items) in a collection. | +| `IfCondition` | Branch: If/else | Runs a set of actions based on a Boolean expression. | +| `SwitchCondition` | Branch: Switch (multiple options) | Runs a set of actions based on the value of a property. | + +## Dialog management + +| Action Name | Action title | Description | +|:-|:-|:-| +| `BeginDialog` | Begin a new dialog | Begins a new dialog and adds it to the stack. You can provide input parameters for the new dialog. When the new dialog ends, control returns to the next step in this trigger. | +| `CancelAllDialogs` | Cancel all active dialogs | Cancels all active dialogs. Optionally sends a custom event that can be caught to prevent cancellation from propagating. | +| `CancelDialog` | Cancel dialog | Cancels the active dialog. Optionally sends a custom event that can be caught to prevent cancellation. | +| `ContinueConversation` | Continue conversation | Sends a proactive message. Requires a bot with a configured storage queue. | +| `ContinueConversationLater` | Continue conversation later | Queues a proactive message to be sent after a delay. Requires the bot to have a storage queue configured. | +| `EndDialog` | End this dialog | Ends the current dialog and returns an optional result. | +| `EndTurn` | End turn | Ends the current turn without explicitly ending the dialog. | +| `GetConversationReference` | Get conversation reference | Saves the current conversation reference to memory. For use with the continue conversation actions. | +| `GotoAction` | Go to action | Jump to another action in the current trigger. | +| `RepeatDialog` | Repeat this dialog | Restarts the current dialog. You can provide input parameters for the dialog. | +| `ReplaceDialog` | Replace this dialog | Replaces the current dialog with a new dialog. You can provide input parameters for the new dialog. | + +## Manage properties + +| Action Name | Action title | Description | +|:-------------------------|:-------------------------|:-------------------------------------------------------------------------------------------------------| +| `DeleteActivity` | Delete Activity | Deletes an activity that was previously sent to a user. | +| `DeleteProperties` | Delete properties | Removes multiple properties at once. | +| `DeleteProperty` | Delete a property | Removes a property from memory. | +| `EditArray` | Edit an array property | Performs an operation on an array. | +| `GetActivityMembers` | Get activity members | Gets the members participating in an activity. Only supported by the BotFrameworkAdapter connector. | +| `GetConversationMembers` | Get conversation members | Gets the members participating in a conversation. Only supported by the BotFrameworkAdapter connector. | +| `SetProperties` | Set properties | Sets the value of multiple properties at once. | +| `SetProperty` | Set a property | Sets a property's value in memory. | +| `UpdateActivity` | Update an activity | Updates an activity that was previously sent to a user. | + +## Access external resources + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +| Action Name | Action title | Description | +|:----------------------|:-----------------------|:-----------------------------------------------------------------------------------------------------------| +| `BeginSkill` | Connect to a skill | Begins a skill and forwards activities to the skill until the skill ends. | +| `EmitEvent` | Emit a custom event | Raises a custom event. To allow a dialog to react to the event, add a custom events trigger to the dialog. | +| `HttpRequest` | Send an HTTP request | Makes an HTTP request to an endpoint. | +| `OAuthInput` | OAuth login | Asks the user to sign in with an OAuth identity provider. | +| `QnAMakerDialog` | QnAMaker dialog | Uses a QnA Maker knowledge base to answer user questions. | +| `SendHandoffActivity` | Send a handoff request | Deprecated. Don't use this action. | +| `SignOutUser` | Sign out user | Signs out the user from an OAuth identity provider. | + +## Debugging options + +| Action Name | Action title | Description | +|:----------------------------|:------------------------|:------------------------------------------------------------------------------------------------------------| +| `LogAction` | Log to console | Writes to the console and optionally sends the message as a trace activity. | +| `TelemetryTrackEventAction` | Telemetry - track event | Uses the registered telemetry client, to track a custom event. | +| `ThrowException` | Throw an exception | Throws an exception. To allow a dialog to catch the exception, add an error occurred trigger to the dialog. | +| `TraceActivity` | Emit a trace event | Sends a trace activity. | + +## Additional Information + +- To learn about actions specific to gathering user input, see the [asking for user input using adaptive dialogs](../adaptive-dialog/adaptive-dialog-prebuilt-inputs.md) article. +- To learn more about adaptive expressions see the [adaptive expressions](../v4sdk/bot-builder-concept-adaptive-expressions.md) article. diff --git a/articles/adaptive-dialog/adaptive-dialog-prebuilt-inputs.md b/articles/adaptive-dialog/adaptive-dialog-prebuilt-inputs.md new file mode 100644 index 000000000..8875603c5 --- /dev/null +++ b/articles/adaptive-dialog/adaptive-dialog-prebuilt-inputs.md @@ -0,0 +1,212 @@ +--- +title: Inputs in adaptive dialogs in Bot Framework SDK +description: Learn about adaptive dialogs prebuilt inputs for collecting and validating user input. +keywords: bot, inputs, adaptive dialogs +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Inputs in adaptive dialogs - reference guide + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +The Bot Framework SDK defines various input dialogs for collecting and validating user input. + +| Input type | Input class | Description | Returns | +| ---------------- | --------------------------------- | -------------------------------------------------------- | ---------------------------------------- | +| Base class | [InputDialog](#inputdialog) | This is the base class that all of the input classes derive from. It defines all shared properties. | An object. | +| Text | [TextInput](#textinput) | Used to ask your users for a **word** or **sentence**. | A string. | +| Number | [NumberInput](#numberinput) | Used to ask your users for a **number**. | A numeric value. | +| Confirmation | [ConfirmInput](#confirminput) | Used to request a **confirmation** from the user. | A Boolean value. | +| Multiple choice | [ChoiceInput](#choiceinput) | Used to ask for a choice from a **set of options**. | The value or index of the selection. | +|File or attachment|[AttachmentInput](#attachmentinput)| Used to request/enable a user to **upload a file**. | A collection of attachment objects. | +| Date or time | [DateTimeInput](#datetimeinput) | Used to ask your users for a **date and or time**. | A collection of date-time objects. | +| Oauth login | [OAuthInput](#oauthinput) | Used to enable your users to **sign into a secure site**.| A token response. | + +## InputDialog + +The input classes provided by the Bot Framework SDK all derive from the base _input dialog_, which derives from the _dialog_ class. All input dialogs have these common properties: + +### AllowInterruptions + +A Boolean expression. `true` to let the parent dialog interrupt the input dialog; otherwise, `false`. + +> [!NOTE] +> The inputs parent dialog can also interrupt. This means that when `AllowInterruptions` is `true`, the recognizer in the inputs parent adaptive dialog will run and its triggers are evaluated. + +### AlwaysPrompt + +A Boolean expression. If `true`, always prompt for input; if `false`, only prompt when the bound [property](#property) is null or empty. + +### DefaultValue + +An adaptive expression representing the default result for the input dialog. If the user input fails for [max turn count](#maxturncount) turns, the input dialog ends and sets the default value to this property. + +### DefaultValueResponse + +The response to send when the users input fails its [Validations](#validations) for [MaxTurnCount](#maxturncount) turns and a [DefaultValue](#defaultvalue) is specified. + +### InvalidPrompt + +The activity template with which to reprompt for input if the user input is recognized but fails validation. (If the input fails for [max turn count](#maxturncount) turns, then the [default value](#defaultvalue) is used and the [default value response](#defaultvalueresponse) is sent.) + +>[!NOTE] +> The `InvalidPrompt` property works only in conjunction with the [Validations](#validations) property. + +### MaxTurnCount + +An integer expression. The maximum number of times to ask for input. If this limit is exceeded, the [default value](#defaultvalue) is used and the [default value response](#defaultvalueresponse) is sent. + +### Prompt + +The activity template with which to initially prompt for user input. + +### Property + +The memory path, or an expression that evaluates to the memory path, of the property to bind the input dialog to. The memory path will be used to get the initial value for the input dialog. It will also be used to store the result of this dialog. Both the `Prompt` and the `Value` property go through recognition and validation steps, so an invalid initial value will result in a prompt. + +Use this to define what property the input dialog is bound to. For example: + +### UnrecognizedPrompt + +The activity template with which to reprompt for input if the user input isn't recognized. (If the input fails for [max turn count](#maxturncount) turns, then the [default value](#defaultvalue) is used and the [default value response](#defaultvalueresponse) is sent.) + +### Validations + +A list of Boolean expressions. Recognized input is invalid if any of these expressions evaluate to `false`. You can use `this.value` to examine the user input in the validation expressions. Validations are expressed using [adaptive expressions][adaptive-expressions] + +### Value + +A string expression. The memory path of the property to get input from each turn. This property will be used as the initial value for the input dialog if the dialog's [property](#property) evaluates to null or empty. If both the dialog's _property_ and _value_ properties evaluate to null or empty, then the dialog will prompt for input. + +Things to keep in mind regarding the `Value` property: + +* The `Value` property is an [adaptive expression][adaptive-expressions]. +* If the expression returns null, the input dialog may attempt to pull data from the input directly. +* If the expression is a value then it will be used as the input. +* The `Value` property allows you to define a how data such as [Recognizer][recognizers] results are bound to the input dialog. + + Examples: + +* To bind the input to any age entity recognized in the input: "=@age" +* To use @age or @number as the input: "=coalesce(@age, @number)" + +> [!TIP] +> You can see an example that uses these `InputDialog` properties in the code sample in the [NumberInput](#numberinput) section below. + +## TextInput + +Use _text input_ when you want to verbatim accept user input as a value for a specific piece of information your bot is trying to collect. Examples include _user's name_ and the _subject of an email_. + +The `TextInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines one additional property: + +* `OutputFormat`: Using [adaptive expressions][adaptive-expressions] you can modify the string, for example, in the code example below the `OutputFormat` expression will capitalize the first letter of each word of the users name. + +## NumberInput + +Asks the user for a number. + +The `NumberInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines these two additional properties: + + + +1. `DefaultLocale`: Sets the default locale for input processing that will be used unless one is passed by the caller. Supported locales are Spanish, Dutch, English, French, German, Japanese, Portuguese, Chinese. +1. `OutputFormat`: Using [adaptive expressions][adaptive-expressions] you can take actions to manipulate the number in some way. For example, you could write an expression to convert a number entered as a temperature given in Fahrenheit to its equivalent Celsius value, perform a mathematical calculation such as adding tax and shipping costs to the value entered, or simply perform a type conversion to specify that the value is either a float or integer as demonstrated in the sample code below. + +## ConfirmInput + +**Confirmation inputs** are useful to use after you ask the user a question and want to confirm their answer. Unlike the **Multiple choice** action that enables your bot to present the user with a list to choose from, confirmation prompts ask the user to make a binary (yes/no) decision. + +The `ConfirmInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines these additional properties: + +1. `ChoiceOptions`: Used to format the presentation of the confirmation choices that are presented to the user, this is an [adaptive expression][adaptive-expressions] that evaluates to a `ChoiceSet` object. This `ChoiceSet` object will only be used as a backup if the initial attempt at recognition of the `ConfirmInput` fails. When the `ConfirmInput` action executes, it first tries to evaluate the input as a Boolean value. If that fails, it makes a second attempt, this time using a choice recognizer evaluating against the ChoiceSet. +1. `ConfirmChoices`: The choices or an [adaptive expression][adaptive-expressions] that evaluates to the choices that will be presented to the user. +1. `DefaultLocale`: Sets the default locale for input processing that will be used unless one is passed by the caller. Supported locales are Spanish, Dutch, English, French, German, Japanese, Portuguese, Chinese +1. `OutputFormat`: The default output format for the `ConfirmInput` action is a boolean. You can override that using the `OutputFormat` property, an [adaptive expressions][adaptive-expressions] which you can use to modify the return results if needed. For example you can use this to cause the `ConfirmInput` action to return a number: `OutputFormat = "if(this.value == true, 1, 0)"`. +If this property is set then the output of the expression is the value returned by the dialog. +1. `Style`: This defines the type of list to present to the user when confirming their input. This uses the `ListStyle` enum which consists of: + 1. `None`: Don't include any choices for prompt. + 1. `Auto`: Automatically select the appropriate style for the current channel. + 1. `Inline`: Add choices to prompt as an inline list. + 1. `List`: Add choices to prompt as a numbered list. + 1. `SuggestedAction`: Add choices to prompt as suggested actions. + 1. `HeroCard`: Add choices to prompt as a HeroCard with buttons. + +## ChoiceInput + +**Choice inputs** are a set of options presented to the user as a **Multiple choice** selection that enables you to present your users with a list of options to choose from. + +The `ChoiceInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines these additional properties: + +1. `ChoiceOptions`: This property is used to format the presentation of the confirmation choices that are presented to the user. +1. `Choices`: An adaptive expression that evaluates to a ChoiceSet that contains the [ordered] list of choices for the user to choose from. +1. `DefaultLocale`: Sets the default locale for input processing that will be used unless one is passed by the caller. Supported locales are Spanish, Dutch, English, French, German, Japanese, Portuguese, Chinese +1. `OutputFormat`: an adaptive expression that evaluates to one of the `ChoiceOutputFormat` enumeration values. +1. `Style`: This defines the type of list to present to the user when confirming their input. This uses the `ListStyle` enum which consists of: + 1. `None`: Don't include any choices for prompt. + 1. `Auto`: Automatically select the appropriate style for the current channel. + 1. `Inline`: Add choices to prompt as an inline list. + 1. `List`: Add choices to prompt as a numbered list. + 1. `SuggestedAction`: Add choices to prompt as suggested actions. + 1. `HeroCard`: Add choices to prompt as a HeroCard with buttons. +1. `RecognizerOptions`: `FindChoicesOptions` or expression which evaluates to `FindChoicesOptions`. The `FindChoicesOptions` has these properties: + 1. `NoValue`: A Boolean value. `true` to search over each choice's _value_ property; otherwise, `false`. The default is `false`. + 1. `NoAction`: A Boolean value. `true` to search over the title of each choice's _action_ property; otherwise, `false`. The default is `false`. + 1. `RecognizeNumbers`: A Boolean value. `true` to allow the input to fall back on using a number recognizer to match against the input choices; otherwise, `false`. The default is `true`. + 1. `RecognizeOrdinals`: A Boolean value. `true` to allow the input to fall back on using an ordinal number recognizer to match against the input choices; otherwise, `false`. The default is `true`. + +## DateTimeInput + +Asks for a date/time. + +The `DateTimeInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines these additional properties: + +1. `DefaultLocale`: Sets the default locale for input processing that will be used unless one is passed by the caller. Supported locales are Spanish, Dutch, English, French, German, Japanese, Portuguese, Chinese. +1. `OutputFormat`: The default output for `DateTimeInput` is an array of `DateTimeResolutions`, this property allows you to define an adaptive expression. Whatever value it returns become the final value for the dialog's `property` property, whether or not it evaluates to a date-time or not. + +## AttachmentInput + +Use to request an attachment from user as input. + +The `AttachmentInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines this additional property: + +* `OutputFormat`: The `AttachmentOutputFormat` or an expression which evaluates to an `AttachmentOutputFormat`. Valid `AttachmentOutputFormat` values are: + 1. `All`: return all attachments as a List. + 1. `First`: return only the first attachment. + +## OAuthInput + +Use to ask user to sign in. + +The `OAuthInput` action inherits all of the properties defined in [InputDialog](#inputdialog) and defines these additional properties: + +1. `ConnectionName`: Name of the OAuth connection configured in Azure AI Bot Service settings page for the bot. +1. `Text`: Additional text to display in the sign-in card. +1. `Title`: Title text to display in the sign-in card. +1. `Timeout`: This is the number of milliseconds `OAuthInput` waits for the user authentication to complete. The default is 900,000 milliseconds, which is 15 minutes. + +The `OAuthInput` action also defines two new methods: + +1. `GetUserTokenAsync`: This method attempts to retrieve the user's token. +1. `SignOutUserAsync`: This method signs out the user. + +The `OAuthInput` action returns a `TokenResponse` object which contains values for `ChannelId`, `ConnectionName`, `Token`, `Expiration`. In the example below, the return value is placed into the `turn` memory scope: `turn.oauth`. You can access values from this as demonstrated in the `LoginSteps()` method: `new SendActivity("Here is your token '${turn.oauth.token}'.")`. + +### Additional information related to OAuth + +The following links provide generalized information on the topic of authentication in the Microsoft Bot Framework SDK. This information isn't tailored or specific to adaptive dialogs. + +* [Bot authentication][authentication] +* [Add authentication to a bot][add-authentication] + +[authentication]:../v4sdk/bot-builder-concept-authentication.md +[add-authentication]:../v4sdk/bot-builder-authentication.md +[recognizers]:/composer/concept-language-understanding +[adaptive-expressions]:../v4sdk/bot-builder-concept-adaptive-expressions.md diff --git a/articles/adaptive-dialog/adaptive-dialog-prebuilt-memory-states.md b/articles/adaptive-dialog/adaptive-dialog-prebuilt-memory-states.md new file mode 100644 index 000000000..bc3ea034f --- /dev/null +++ b/articles/adaptive-dialog/adaptive-dialog-prebuilt-memory-states.md @@ -0,0 +1,143 @@ +--- +title: Managing state in adaptive dialogs - reference guide +description: Describing memory scopes in adaptive dialogs +keywords: bot, managing state, memory scopes, user scope, conversation scope, dialog scope, settings scope, adaptive dialogs +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: concept-article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Managing state in adaptive dialogs - reference guide + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article provides technical details that will help you work with memory scopes in adaptive dialogs. For an introduction to memory scopes and managing state in adaptive dialogs, see the [Conversation flow and memory][managing-state] Composer concept article. + +> [!TIP] +> All property paths are case-insensitive. For example, `user.name` is the same as `user.Name`. Also, if you don't have a property named `user.name` and you create a property named `user.name.first` the `user.name` object will automatically be created for you. + +## User scope + +User scope is persistent data scoped to the ID of the user you're conversing with. + +Examples: + +- `user.name` +- `user.address.city` + +## Conversation scope + +Conversation scope is persistent data scoped to the ID of the conversation you're having. + +Examples: + +- `conversation.hasAccepted` +- `conversation.dateStarted` +- `conversation.lastMaleReference` +- `conversation.lastFemaleReference` +- `conversation.lastLocationReference` + +## Dialog scope + +Dialog scope persists data for the life of the associated dialog, providing memory space for each dialog to have internal persistent bookkeeping. Dialog scope is cleared when the associated dialog ends. + +Dialog scope shorthand examples: + +- The shorthand for `dialog.orderStarted` is `$orderStarted`. +- The shorthand for `dialog.shoppingCart` is `$shoppingCart`. + +All options passed into `BeginDialog` when creating a new adaptive dialog become properties of that dialog and can be accessed as long as it's in scope. You access these properties by name: dialog.\. For example, if the caller passed {a : '1', b: '2'} then they'll be set as dialog.a and dialog.b. + +### Dialog subscopes + +All trigger actions in an adaptive dialog have their own subscopes and are accessed by name. For example, the `Foreach` action is accessed as `dialog.Foreach`. By default, the index and value are set in the `dialog.foreach` scope, which can be accessed as `dialog.Foreach.index` and `dialog.Foreach.value`. + +## Turn scope + +The turn scope contains _non-persistent_ data that is only scoped for the current turn. The turn scope provides a place to share data for the lifetime of the current turn. + +Examples: + +- `turn.bookingConfirmation` +- `turn.activityProcessed` + +### Turn subscopes + +#### turn.activity + +Each incoming [activity][botframework-activity] to the bot is available via `turn.activity` scope. + +For example, you might have something like this defined in our .lg file to respond to a user that entered an invalid value when prompted for their age: + +```lg +Sorry, I don't understand '${turn.activity.text}'. ${GetAge()} +``` + +#### turn.recognized + +All intents and entities returned from a [recognizer][recognizers] on any given turn, are automatically set in the `turn.recognized` scope and remain available until the next turn occurs. the `turn.recognized` scope has three properties: + +- `turn.recognized.intents.xxx`: A list of the top intents classified by the recognizer for that turn. +- `turn.recognized.entities.xxx`: A list of entities recognized that turn. +- `turn.recognized.score`: The _confidence score_ of the top scoring intent for that turn. + +#### turn.dialogEvent + +`turn.dialogEvent` contains the payload of an event raised either by the system or your code. You can access the information contained in the payload by accessing the `turn.dialogEvent..value` scope. + +#### turn.lastResult + + You can access the results from the last dialog that was called from the `turn.lastResult` scope. + +#### turn.activityProcessed + +`turn.activityProcessed`, a Boolean property; `true` indicates that the `turnContext.activity` has been consumed by some component in the system. + +#### turn.interrupted + +`turn.interrupted`, a Boolean property; `true` indicates that an interruption has occurred. + +## Settings scope + +This represents any settings that are made available to the bot via the platform specific settings configuration system, for example if you're developing your bot using C#, these settings will appear in the **appsettings.json** file. + +### Settings scope example + +This is an example of an **appsettings.json** file that holds configuration settings for your bot: + +```json +{ + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + "QnAMaker": { + "knowledgebaseId": "", + "hostname": "https://.azurewebsites.net/qnamaker", + "endpointKey": "yourEndpointKey" + } +} +``` + +## This scope + +The `this` scope pertains the active action's property bag. This is helpful for input actions since their life type typically lasts beyond a single turn of the conversation. + +- `this.value` holds the current recognized value for the input. +- `this.turnCount` holds the number of times the missing information has been prompted for this input. + +## Class scope + +This holds the instance properties of the active dialog. you reference this scope as follows: `${class.}`. + +## Additional information + +- For an introduction to managing state in Composer, see the [Conversation flow and memory][managing-state] Composer concept article. + +[managing-state]: /composer/concept-memory +[botframework-activity]: https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md +[recognizers]: ../v4sdk/bot-builder-concept-adaptive-dialog-recognizers.md diff --git a/articles/adaptive-dialog/adaptive-dialog-prebuilt-recognizers.md b/articles/adaptive-dialog/adaptive-dialog-prebuilt-recognizers.md new file mode 100644 index 000000000..8308b2d42 --- /dev/null +++ b/articles/adaptive-dialog/adaptive-dialog-prebuilt-recognizers.md @@ -0,0 +1,148 @@ +--- +title: Prebuilt recognizers for adaptive dialogs +description: Adaptive dialogs and language recognizers work together to interpret user intent and to react fluidly to user input. This article describes builtin recognizers in the Bot Framework SDK. +keywords: bot, recognizers, adaptive dialogs +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - abs-meta-21q1 + - evergreen +monikerRange: 'azure-bot-service-4.0' +--- + +# Recognizers in adaptive dialogs + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Language recognizers let your bot interpret user input. Adaptive dialogs and language recognizers work together to interpret user intent and to react fluidly to user input. This article describes the builtin recognizers in the Bot Framework SDK and some of their key properties. + +For information about how recognizer are used, see [Language understanding](/composer/concept-language-understanding) in the Bot Framework Composer documentation. + +## Cross-trained recognizer set + +The cross-trained recognizer set compares recognition results from more than one recognizer to decide a winner. Given a collection of recognizers, the cross-trained recognizer will: + +* Promote the recognition result of one of the recognizers if all other recognizers defer recognition to a single recognizer. To defer recognition, a recognizer can return the `None` intent or an explicit `DeferToRecognizer_recognizerId` as intent. +* Raise an `OnChooseIntent` event to allow your code to choose which recognition result to use. Each recognizer's results are returned via the `turn.recognized.candidates` property. This enables you to choose the most appropriate result. + +## Default recognizer + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + +The default recognizer was created to replace the following recognizers: + +* **LUIS recognizer** - to extract intents and entities from a user's utterance based on the defined Language Understanding (LUIS) service. +* **QnA Maker recognizer** - to extract intents from a user's utterance based on the defined QnA Maker service. +* **Cross-trained recognizer set** - to compare recognition results from more than one recognizer to decide a winner. + +## LUIS recognizer + +Language Understanding (LUIS) is a cloud-based API service that applies custom machine-learning intelligence to a user's conversational, natural language text to predict overall meaning, and pull out relevant, detailed information. The LUIS recognizer enables you to extract intents and entities from a user's utterance based on the defined LUIS application, which you train in advance. + +> [!TIP] +> For more information about how to incorporate language understanding into your bot using LUIS, see: +> +> * [Add LUIS for language understanding][update-the-recognizer-type-to-luis] +> * [LUIS.ai][4] is a machine learning-based service that enables you to build natural language capabilities into your bot. +> * [What is LUIS][5] +> * [Language Understanding][6] +> * [.lu file format][7] +> * [Adaptive expressions][8] + +## Multi-language recognizer + +When building a sophisticated multi-lingual bot, you'll typically have one recognizer for each language and locale. The Multi-language recognizer enables you to easily specify the recognizer to use based on the [locale][3] property on the incoming activity from a user. + +For more information, see the [Multilingual support](/composer/how-to-use-multiple-language) article in the Composer documentation. + +## Orchestrator recognizer + +[Orchestrator][] is a language understanding solution optimized for conversational AI applications. It replaces the Bot Framework Dispatcher. The Orchestrator recognizer enables you to extract an intent from a user's utterance, which could be used to route to an appropriate skill or recognizer, such as LUIS or QnA Maker. + +> [!TIP] +> For more information about how to incorporate language understanding into your bot using Orchestrator, see: +> +> * [What is Orchestrator][Orchestrator] +> * [BF Orchestrator CLI][15] + +## QnA Maker recognizer + +[QnAMaker.ai][12] is one of the [Azure AI services][13] that enables you to create rich question-answer pairs from existing content - documents, URLs, PDFs, and so on. You can use the QnA Maker recognizer to integrate with the service. + +> [!NOTE] +> The QnA Maker recognizer will emit a `QnAMatch` event, which you can handle with an `OnQnAMatch` trigger. +> The entire QnA Maker response will be available in the `answer` property. + +## Recognizer set + +Sometimes you might need to run more than one recognizer on every turn of the conversation. The recognizer set does exactly that. All recognizers are run on each turn of the conversation and the result is a union of all recognition results. + +## Regular expression (regex) recognizer + +The _Regex recognizer_ uses regular expressions to extract intent and entity data from an utterance. + +The Regex recognizer consists primarily of: + +* `Intents`. The `Intents` object contains a list of `IntentPattern` objects, and these `IntentPattern` objects consist of an `Intent` property that is the name of the intent, and a `Pattern` property that contains a regular expression used to parse the utterance to determine intent. +* `Entities`. The `Entities` object contains a list of `EntityRecognizer` objects. The Bot Framework SDK defines several `EntityRecognizer` classes to help you determine the entities contained in a user's utterance: + * `AgeEntityRecognizer` + * `ConfirmationEntityRecognizer` + * `CurrencyEntityRecognizer` + * `DateTimeEntityRecognizer` + * `DimensionEntityRecognizer` + * `EmailEntityRecognizer` + * `EntityRecognizer` + * `EntityRecognizerSet` + * `GuidEntityRecognizer` + * `HashtagEntityRecognizer` + * `IpEntityRecognizer` + * `MentionEntityRecognizer` + * `NumberEntityRecognizer` + * `NumberRangeEntityRecognizer` + * `OrdinalEntityRecognizer` + * `PercentageEntityRecognizer` + * `PhoneNumberEntityRecognizer` + * `RegExEntityRecognizer` + * `TemperatureEntityRecognizer` + * `TextEntity` + * `TextEntityRecognizer` + * `UrlEntityRecognizer` + +> [!TIP] +> +> * The Regex recognizer emits a "None" intent when the input utterance doesn't match any defined intent. You can create an `OnIntent` trigger with `Intent = "None"` to handle this scenario. +> * The Regex recognizer is useful for testing and quick prototyping. For more sophisticated bots we recommend using the Language Understanding (LUIS) recognizer. +> * You might find the [Regular expression language quick reference][2] helpful. + +## Additional Information + +* [What is LUIS][5] +* [Language Understanding][6] +* [.lu file format][7] +* [Adaptive expressions][8] +* [Extract data from utterance text with intents and entities][9] +* [Add natural language understanding (LU) to your bot][10] +* [Add natural language generation (LG) to your bot][1] + + +[1]:../v4sdk/bot-builder-concept-adaptive-dialog-generators.md +[2]:/dotnet/standard/base-types/regular-expression-language-quick-reference +[3]:https://github.com/microsoft/botbuilder/blob/master/specs/botframework-activity/botframework-activity.md#locale +[4]:https://luis.ai +[5]:/azure/ai-services/luis/what-is-luis +[6]:../v4sdk/bot-builder-concept-luis.md +[7]:../file-format/bot-builder-lu-file-format.md +[8]:../v4sdk/bot-builder-concept-adaptive-expressions.md +[9]:/azure/ai-services/luis/luis-concept-data-extraction?tabs=V2 +[10]:../v4sdk/bot-builder-howto-v4-luis.md +[12]:https://qnamaker.ai +[13]:https://azure.microsoft.com/products/ai-services/ +[Orchestrator]:/composer/concept-orchestrator +[15]:https://github.com/microsoft/botframework-cli/tree/main/packages/orchestrator +[update-the-recognizer-type-to-luis]: /composer/how-to-add-luis#update-the-recognizer-type-to-luis diff --git a/articles/adaptive-dialog/adaptive-dialog-prebuilt-triggers.md b/articles/adaptive-dialog/adaptive-dialog-prebuilt-triggers.md new file mode 100644 index 000000000..a81f4fe82 --- /dev/null +++ b/articles/adaptive-dialog/adaptive-dialog-prebuilt-triggers.md @@ -0,0 +1,99 @@ +--- +title: Events and triggers for adaptive dialogs +description: Describes the adaptive dialog prebuilt triggers. Triggers handle dialog specific events that are related to the lifecycle of the dialog. +keywords: bot, triggers, adaptive dialogs +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Events and triggers in adaptive dialogs + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +For an introduction to this topic, see the [Triggers](/composer/concept-events-and-triggers) concept article in the Composer documentation. + +## Recognizer event triggers + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +| Event cause | Trigger name | Base event | Description | +| ------------------------- | ------------- | ------------- | ----------------------------------------------------------------- | +| Choose Intent | `OnChooseIntent` |`ChooseIntent` | This trigger is run when ambiguity has been detected between intents from multiple recognizers in a [CrossTrainedRecognizerSet][recognizers-cross-trained-recognizer-set].| +| Intent recognized| `OnIntent` | `RecognizedIntent` | Actions to perform when specified intent is recognized. | +|QnAMatch intent|`OnQnAMatch`| `RecognizedIntent` |This trigger is run when the [QnAMakerRecognizer][qna-maker-recognizer] has returned a `QnAMatch` intent. The entity `@answer` will have the `QnAMaker` answer.| +|Unknown intent recognized| `OnUnknownIntent` | `UnknownIntent` | Actions to perform when user input is unrecognized or no match is found in any of the `OnIntent` triggers. You can also use this as your first trigger in your root dialog in place of the `OnBeginDialog` to perform any needed tasks when the dialog first starts. | + +The `OnIntent` trigger lets you handle the `recognizedIntent` event. The `recognizedIntent` event is raised by a recognizer. Except for the [QnA Maker recognizer][qna-maker-recognizer], all of the Bot Framework SDK built-in recognizers emit this event when they successfully identify a user _input_ so that your bot can respond appropriately. + +Use the `OnUnknownIntent` trigger to catch and respond when a `recognizedIntent` event isn't caught and handled by any of the other triggers. This means that any unhandled intent (including "none") can cause it to trigger, but only if there aren't any currently executing actions for the dialog. Use the `OnUnknownIntent` trigger to catch and respond when a "none" intent occurs. Using the `OnIntent` trigger to handle a "none" intent can produce unexpected results. + +## Dialog event triggers + +Dialog triggers handle dialog specific events that are related to the _lifecycle_ of the dialog. There are currently six dialog triggers in the Bot Framework SDK, and they all derive from the `OnDialogEvent` class. + +> [!TIP] +> These aren't like normal interruption event handlers where the child's actions will continue running after the handler's actions complete. For all of the events below, the bot will run a new set of actions and will end the turn once those actions have finished. + +| Trigger name | Base event | Description | +| ---------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| `OnBeginDialog` | `BeginDialog` | Actions to perform when this dialog begins. For use with child dialogs only, not to be used in your root dialog, In root dialogs, use `OnUnknownIntent` to perform dialog initialization activities.| +| `OnCancelDialog` | `CancelDialog` | This event allows you to prevent the current dialog from being canceled due to a child dialog executing a `CancelAllDialogs` action. | +| `OnEndOfActions` | `EndOfActions` | This event occurs once all actions and ambiguity events have been processed. | +| `OnError` | `Error` | Actions to perform when an `Error` dialog event occurs. This event is similar to `OnCancelDialog` in that you're preventing the adaptive dialog that contains this trigger from ending, in this case due to an error in a child dialog.| +| `OnRepromptDialog` |`RepromptDialog`| Actions to perform when `RepromptDialog` event occurs. | +| `OnDialog` | `DialogEvents.VersionChanged` | | + +## Activity event triggers + +Activity triggers let you associate actions to any incoming activity from the client such as when a new user joins and the bot begins a new conversation. Additional information on activities can be found in [Bot Framework Activity schema][botframework-activity]. + +All activity events have a base event of `ActivityReceived` and are further refined by their _activity type_. The Base class that all activity triggers derive from is `OnActivity`. + +| Event cause | ActivityType | Trigger name | Description | +| ------------------- | -------------- | ------------------------------ | --------------------------------------------------------------------------------- | +| Greeting | `ConversationUpdate` | `OnConversationUpdateActivity` | Actions to perform on receipt of a `conversationUpdate` activity, when the bot or a user joins or leaves a conversation. | +| Conversation ended | `EndOfConversation` | `OnEndOfConversationActivity` | Actions to perform on receipt of an `endOfConversation` activity. | +| Event received | `Event` | `OnEventActivity` | Actions to perform on receipt of an `event` activity. | +| Handover to human | `Handoff` | `OnHandoffActivity` | Actions to perform on receipt of a `handOff` activity. | +| Conversation invoked| `Invoke` | `OnInvokeActivity` | Actions to perform on receipt of an `invoke` activity. | +| User is typing | `Typing` | `OnTypingActivity` | Actions to perform on receipt of a `typing` activity. | + +## Message event triggers + +Message event triggers allow you to react to any message event such as when a message is updated (`MessageUpdate`) or deleted (`MessageDeletion`) or when someone reacts (`MessageReaction`) to a message (for example, some of the common message reactions include a Like, Heart, Laugh, Surprised, Sad and Angry reactions). + +Message events are a type of activity event and, as such, all message events have a base event of `ActivityReceived` and are further refined by _activity type_. The Base class that all message triggers derive from is `OnActivity`. + +| Event cause | ActivityType | Trigger name | Description | +|--|--|--|--| +| Message received | `Message` | `OnMessageActivity` | Actions to perform on receipt of an activity with type `MessageReceived`. | +| Message deleted | `MessageDeletion` | `OnMessageDeleteActivity` | Actions to perform on receipt of an activity with type `MessageDelete`. | +| Message reaction | `MessageReaction` | `OnMessageReactionActivity` | Actions to perform on receipt of an activity with type `MessageReaction`. | +| Message updated | `MessageUpdate` | `OnMessageUpdateActivity` | Actions to perform on receipt of an activity with type `MessageUpdate`. | + +## Custom event trigger + +You can emit your own events by adding the [EmitEvent][emitevent] action to any trigger, then you can handle that custom event in any trigger in any dialog in your bot by defining a _custom event_ trigger. A custom event trigger is the `OnDialogEvent` trigger that in effect becomes a custom trigger when you set the `Event` property to the same value as the EmitEvent's `EventName` property. + +> [!TIP] +> You can allow other dialogs in your bot to handle your custom event by setting the EmitEvent's `BubbleEvent` property to true. + +| Event cause | Trigger name | Base class | Description | +| ------------ | --------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Custom event | `OnDialogEvent` | `OnCondition` | Actions to perform when a custom event is detected. Use [Emit a custom event][emitevent] action to raise a custom event. | + +[triggers]:../v4sdk/bot-builder-concept-adaptive-dialog-triggers.md +[inputs]:../v4sdk/bot-builder-concept-adaptive-dialog-inputs.md + +[recognizers-cross-trained-recognizer-set]:../v4sdk/bot-builder-concept-adaptive-dialog-recognizers.md#cross-trained-recognizer-set +[qna-maker-recognizer]:adaptive-dialog-prebuilt-recognizers.md#qna-maker-recognizer +[emitevent]: /composer/concept-events-and-triggers#custom-events + +[botframework-activity]:https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md diff --git a/articles/adaptive-expressions/adaptive-expressions-API-reference.md b/articles/adaptive-expressions/adaptive-expressions-API-reference.md new file mode 100644 index 000000000..c2df70902 --- /dev/null +++ b/articles/adaptive-expressions/adaptive-expressions-API-reference.md @@ -0,0 +1,25 @@ +--- +title: API reference for Adaptive expressions - Bot Service +description: API reference for Adaptive expressions +keywords: adaptive expressions, api reference +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# API reference for Adaptive expressions + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +## API reference links + +Adaptive expressions are language-agnostic. API references are available for bot developers working with adaptive expressions in the following languages: + +- [C#](/dotnet/api/adaptiveexpressions) +- [JavaScript](/javascript/api/adaptive-expressions) diff --git a/articles/adaptive-expressions/adaptive-expressions-prebuilt-functions.md b/articles/adaptive-expressions/adaptive-expressions-prebuilt-functions.md new file mode 100644 index 000000000..2bf2aa0e1 --- /dev/null +++ b/articles/adaptive-expressions/adaptive-expressions-prebuilt-functions.md @@ -0,0 +1,5846 @@ +--- +title: Adaptive expressions prebuilt functions in Bot Framework SDK +description: Learn about the available prebuilt functions in adaptive expressions ordered by their general purpose. +keywords: adaptive expressions, prebuilt functions, reference +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Adaptive expressions prebuilt functions + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article lists the available prebuilt functions ordered by their general purpose. For information about operators used in prebuilt functions and expression syntax, see [Operators](../v4sdk/bot-builder-concept-adaptive-expressions.md#operators). + +Prebuilt expressions are divided into the following function types: + +- [String](#string-functions) +- [Collection](#collection-functions) +- [Logical comparison](#logical-comparison-functions) +- [Conversion](#conversion-functions) +- [Math](#math-functions) +- [Date](#date-and-time-functions) +- [Timex](#timex-functions) +- [URI parsing](#uri-parsing-functions) +- [Object manipulation and construction](#object-manipulation-and-construction-functions) +- [Regular expression](#regular-expression-functions) +- [Type checking](#type-checking-functions) + +You can also view the list in [alphabetical order](#prebuilt-functions-sorted-alphabetically). + +## String functions + +|Function |Explanation| +|-----------|-----------| +|[length](#length)|Return the length of a string.| +|[replace](#replace)|Replace a substring with the specified string and return the updated string. This function is case-sensitive.| +|[replaceIgnoreCase](#replaceIgnoreCase)| Replace a substring with the specified string, and return the updated string. This function is case-insensitive.| +|[split](#split) |Return an array that contains substrings based on the delimiter specified.| +|[substring](#substring)|Return characters from a string.| +|[toLower](#toLower)|Return a string in lowercase in an optional locale format.| +|[toUpper](#toUpper)|Return a string in uppercase in an optional locale format.| +|[trim](#trim)|Remove leading and trailing white spaces from a string.| +|[addOrdinal](#addOrdinal)|Return the ordinal number of the input number.| +|[endsWith](#endsWith)|Check whether a string ends with a specific substring. Return `true` if the substring is found, or return `false` if not found. This function is case-insensitive.| +|[startsWith](#startsWith)|Check whether a string starts with a specific substring. Return `true` if the substring is found, or return `false` if not found. This function is case-insensitive.| +|[countWord](#countWord)|Return the number of words in the given string.| +|[concat](#concat)|Combine two or more strings and return the resulting string.| +|[newGuid](#newGuid)|Return a new Guid string.| +|[indexOf](#indexOf)|Return the starting position or index value of a substring **or** searches for the specified object and return the zero-based index of the first occurrence within the entire list. This function is case-insensitive, and indexes start with the number 0.| +|[lastIndexOf](#lastIndexOf)|Return the starting position or index value of the last occurrence of a substring **or** search for the specified object and return the zero-based index of the last occurrence within the range of elements in the list.This function is case-insensitive, and indexes start with the number 0.| +|[sentenceCase](#sentenceCase)|Capitalize the first letter of the first word in a string in an optional local format.| +|[titleCase](#titleCase)|Capitalize the first letter of each word in a string in an optional locale format.| +|[reverse](#reverse)|Reverse the order of the elements in a string or array.| + +## Collection functions + +|Function |Explanation| +|-----------|-----------| +|[contains](#contains) |Works to find an item in a string, to find an item in an array, or to find a parameter in a complex object.
**Examples**:
contains('hello world', 'hello')
contains(createArray('1','2'), '1')
contains(json("{'foo':'bar'}"), 'foo')| +|[first](#first)|Return the first item from the collection.| +|[join](#join) |Return a string that has all the items from an array and has each character separated by a delimiter.
**Example**:
join(createArray('a','b'), '.') = "a.b"| +|[last](#last) |Return the last item from the collection.| +|[count](#count)|Return the number of items in the collection.| +|[foreach](#foreach)|Operate on each element and return the new collection.| +|[union](#union)|Return a collection that has all the items from the specified collections.| +|[skip](#skip)|Remove items from the front of a collection, and return the remaining items.| +|[take](#take)|Return items from the front of a collection.| +|[intersection](#intersection)|Return a collection that has only the common items across the specified collections.| +|[subArray](#subArray)|Return a subarray from specified start and end position. Index values start with the number 0.| +|[select](#select)|Operate on each element and return the new collection of transformed elements.| +|[where](#where)|Filter on each element and return the new collection of filtered elements which match the specific condition.| +|[sortBy](#sortBy)|Sort elements in the collection in ascending order and return the sorted collection.| +|[sortByDescending](#sortByDescending)|Sort elements in the collection in descending order and return the sorted collection.| +|[indicesAndValues](#indicesAndValues)|Turn an array or object into an array of objects with index and value property.| +|[flatten](#flatten)|Flatten arrays into an array with non-array values.| +|[unique](#unique)|Remove all duplicates from an array.| +|[any](#any) | Determines whether any elements of a sequence satisfy a condition.| +|[all](#all) | Determine whether all elements of a sequence satisfy a condition.| +|[reverse](#reverse)|Reverse the order of the elements in a string or array.| +|[merge](#merge)| Merges multiple JSON objects or items in an array together.| + +## Logical comparison functions + +|Function|Explanation| +|-----------|-----------| +|[and](#and)|Logical and. Return true if all specified expressions evaluate to true.| +|[equals](#equals)|Comparison equal. Return true if specified values are equal.| +|[empty](#empty)|Check if the target is empty.| +|[greater](#greater)|Comparison greater than. Return `true` if the first value is more, or return `false` if less.| +|[greaterOrEquals](#greaterOrEquals)|Comparison greater than or equal to. Return `true` if the first value is greater or equal, or return `false` if the first value is less.| +|[if](#if)|Check whether an expression is true or false. Based on the result, return a specified value.| +|[less](#less)| Comparison less than operation. Return `true` if the first value is less, or return `false` if the first value is more.| +|[lessOrEquals](#lessOrEquals)| Comparison less than or equal operation. Return `true` if the first value is less than or equal, or return `false` if the first value is more.| +|[not](#not)| Logical not operator. Return `true` if the expression is false, or return `false` if true.| +|[or](#or)|Logical or operation. Return `true` if at least one expression is true, or return `false` if all are false.| +|[exists](#exists)|Evaluate an expression for truthiness.| + +## Conversion functions + +|Function|Explanation| +|-----------|-----------| +|[float](#float)|Return the floating point representation of the specified string. | +|[int](#int)|Return the integer representation of the specified string. | +|[string](#string)|Return the string version of the specified value in an optional locale format.| +|[bool](#bool)|Return the Boolean representation of the specified string.| +|[createArray](#createArray)|Create an array from multiple inputs.| +|[json](#json) |Return the JavaScript Object Notation (JSON) type value or object of a string or XML.| +|[base64](#base64)|Return the base64-encoded version of a string or byte array.| +|[base64ToBinary](#base64ToBinary)|Return the binary version for a base64-encoded string.| +|[base64ToString](#base64ToString)|Return the string version of a base64-encoded string.| +|[binary](#binary)|Return the binary version for an input value.| +|[dataUri](#dataUri)|Return the URI for an input value.| +|[dataUriToBinary](#dataUriToBinary)|Return the binary version of a data URI.| +|[dataUriToString](#dataUriToString)|Return the string version of a data URI.| +|[uriComponent](#uriComponent)|Return the URI-encoded version for an input value by replacing URL-unsafe characters with escape characters.| +|[uriComponentToString](#uriComponentToString)|Return the string version of a URI-encoded string.| +|[xml](#xml)|Return the XML version of a string.| +|[formatNumber](#formatNumber)|Format a value to the nearest number to the specified number of fractional digits and an optional specified locale.| +|[jsonStringify](#jsonStringify) | Return the JSON string of a value. | +|[stringOrValue](#stringOrValue) Wrap string interpolation to get the real value. For example, `stringOrValue('${1}')` returns the number 1, while `stringOrValue('${1} item')` returns the string "1 item".| + +## Math functions + +|Function|Explanation| +|-----------|-----------| +|[abs](#abs)| Returns the absolute value of the specified number.| +|[add](#add)|Mathematical and. Return the result from adding two numbers (pure number case) or concatenating two or more strings.| +|[div](#div)|Mathematical division. Return the integer result from dividing two numbers.| +|[max](#max)|Return the largest value from a collection.| +|[min](#min)|Return the smallest value from a collection.| +|[mod](#mod)|Return the remainder from dividing two numbers.| +|[mul](#mul)|Mathematical multiplication. Return the product from multiplying two numbers.| +|[rand](#rand)|Return a random number between specified min and max value.| +|[sqrt](#sqrt)| Return the square root of a specified number. | +|[sub](#sub)|Mathematical subtraction. Return the result from subtracting the second number from the first number.| +|[sum](#sum)|Return the sum of numbers in an array.| +|[range](#range)|Return an integer array that starts from a specified integer.| +|[exp](#exp)|Return exponentiation of one number to another.| +|[average](#average)|Return the average number of an numeric array.| +|[floor](#floor)|Return the largest integral value less than or equal to the specified number.| +|[ceiling](#ceiling)|Return the smallest integral value greater than or equal to the specified number.| +|[round](#round)|Round a value to the nearest integer or to the specified number of fractional digits.| + +## Date and time functions + +|Function|Explanation| +|-----------|-----------| +|[addDays](#addDays)|Add a number of specified days to a given timestamp in an optional locale format.| +|[addHours](#addHours)|Add a specified number of hours to a given timestamp in an optional locale format.| +|[addMinutes](#addMinutes)|Add a specified number of minutes to a given timestamp in an optional locale format.| +|[addSeconds](#addSeconds)|Add a specified number of seconds to a given timestamp.| +|[dayOfMonth](#dayOfMonth)|Return the day of a month for a given timestamp or Timex expression.| +|[dayOfWeek](#dayOfWeek)|Return the day of the week for a given timestamp.| +|[dayOfYear](#dayOfYear)|Return the day of the year for a given timestamp.| +|[formatDateTime](#formatDateTime)|Return a timestamp in an optional locale format.| +|[formatEpoch](#formatEpoch)|Return a timestamp in an optional locale format from UNIX Epoch time (Unix time, POSIX time).| +|[formatTicks](#formatTicks)|Return a timestamp in an optional locale format from ticks.| +|[subtractFromTime](#subtractFromTime)|Subtract a number of time units from a timestamp in an optional locale format.| +|[utcNow](#utcNow)|Return the current timestamp in an optional locale format as a string.| +|[dateReadBack](#dateReadBack)|Use the date-time library to provide a date readback.| +|[month](#month)|Return the month of given timestamp.| +|[date](#date)|Return the date for a given timestamp.| +|[year](#year)|Return the year for the given timestamp.| +|[getTimeOfDay](#getTimeOfDay)|Return the time of day for a given timestamp. | +|[getFutureTime](#getFutureTime)|Return the current timestamp in an optional locale format plus the specified time units. | +|[getPastTime](#getPastTime)|Return the current timestamp in an optional locale format minus the specified time units. | +|[addToTime](#addToTime) |Add a number of time units to a timestamp in an optional locale format. | +|[convertFromUTC](#convertFromUTC) |Convert a timestamp in an optional locale format from Universal Time Coordinated (UTC). | +|[convertToUTC](#convertToUTC) |Convert a timestamp in an optional locale format to Universal Time Coordinated (UTC). | +|[startOfDay](#startOfDay) |Return the start of the day for a timestamp in an optional locale format.| +|[startOfHour](#startOfHour) |Return the start of the hour for a timestamp in an optional locale format. | +|[startOfMonth](#startOfMonth) |Return the start of the month for a timestamp in an optional locale format.| +|[ticks](#ticks) |Return the ticks property value of a specified timestamp.| +|[ticksToDays](#ticksToDays)| Convert a ticks property value to the number of days. | +|[ticksToHours](#ticksToHours)| Convert a ticks property value to the number of hours. | +|[ticksToMinutes](#ticksToMinutes)| Convert a ticks property value to the number of minutes. | +|[dateTimeDiff](#dateTimeDiff)| Return the difference in ticks between two timestamps. | +|[getPreviousViableDate](#getPreviousViableDate) | Return the previous viable date of a Timex expression based on the current date and an optionally specified timezone. | +|[getNextViableDate](#getNextViableDate) | Return the next viable date of a Timex expression based on the current date and an optionally specified timezone. | +|[getPreviousViableTime](#getPreviousViableTime) | Return the previous viable time of a Timex expression based on the current time and an optionally specified timezone. | +|[getNextViableTime](#getNextViableTime) | Return the next viable time of a Timex expression based on the current time and an optionally specified timezone. | + +## Timex functions + +|Function |Explanation| +|-----------|-----------| +|[isPresent](#isPresent) | Return true if the TimexProperty or Timex expression refers to the present. | +|[isDuration](#isDuration) | Return true if the TimexProperty or Timex expression refers to a duration. | +|[isTime](#isTime) | Return true if the TimexProperty or Timex expression refers to a time. | +|[isDate](#isDate) | Return true if the TimexProperty or Timex expression refers to a date. | +|[isTimeRange](#isTimeRange) | Return true if the TimexProperty or Timex expression refers to a time range. | +|[isDateRange](#isDateRange) | Return true if the TimexProperty or Timex expression refers to a date range. | +|[isDefinite](#isDefinite) | Return true if the TimexProperty or Timex expression refers to a definite day. | +|[resolve](#resolve) | Return a string of a given TimexProperty or Timex expression if it refers to a valid time. Valid time contains hours, minutes, and seconds. | + +## URI parsing functions + +|Function|Explanation| +|-----------|-----------| +|[uriHost](#uriHost) |Return the host value of a uniform resource identifier (URI).| +|[uriPath](#uriPath) |Return the path value of a uniform resource identifier (URI). | +|[uriPathAndQuery](#uriPathAndQuery) |Return the path and query values for a uniform resource identifier (URI). | +|[uriPort](#uriPort) |Return the port value of a uniform resource identifier (URI).| +|[uriQuery](#uriQuery) |Retur0sn the query value of a uniform resource identifier (URI).| +|[uriScheme](#uriScheme)|Return the scheme value of a uniform resource identifier (URI). | + +## Object manipulation and construction functions + +|Function|Explanation| +|-----------|-----------| +|[addProperty](#addProperty) |Add a property and its value, or name-value pair, to a JSON object and return the updated object.| +|[removeProperty](#removeProperty) |Remove a property from JSON object and return the updated object.| +|[setProperty](#setProperty) |Set the value of a JSON object's property and return the updated object.| +|[getProperty](#getProperty) |Return the value of a specified property or root property from a JSON object. | +|[coalesce](#coalesce) |Return the first non-null value from one or more parameters. | +|[xPath](#xPath) |Check XML for nodes or values that match an XPath (XML Path Language) expression, and return the matching nodes or values.| +|[jPath](#jPath) |Check JSON or a JSON string for nodes or value that match a path expression, and return the matching nodes.| +|[setPathToValue](#setPathToValue) |Set the value of a specific path and return the value.| + +## Regular expression functions + +|Function|Explanation| +|-----------|-----------| +|[isMatch](#isMatch)|Return true if a string matches a common regex pattern.| + +## Type checking functions + +|Function|Explanation| +|-----------|-----------| +|[EOL](#EOL)| Return the end of line (EOL) sequence text.| +|[isInteger](#isInteger)|Return true if given input is an integer number| +|[isFloat](#isFloat)|Return true if the given input is a float point number| +|[isBoolean](#isBoolean)|Return true if the given input is a Boolean.| +|[isArray](#isArray)|Return true if the given input is an array.| +|[isObject](#isObject)|Return true if the given input is an object.| +|[isDateTime](#isDateTime)|Return true if the given input is a UTC ISO format timestamp.| +|[isString](#isString)|Returns **true** if the given input is a string.| + +## Prebuilt functions sorted alphabetically + + + +### abs + +Return the absolute value of the specified number. + +``` +abs() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | Number to get absolute value of | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result*> | number| The result from computing the absolute value.| + +*Examples* + +These examples compute the absolute value: + +``` +abs(3.12134) +abs(-3.12134) +``` + +And both return the result **3.12134**. + + + +### add + +Return the result from adding two or more numbers (pure number case) or concatenating two or more strings (other case). + +``` +add(, , ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*item1*>, <*item2*>,... | Yes | any | items | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result-sum*> | number or string | The result from adding the specified numbers or the concat result.| + +*Example* + +This example adds the specified numbers: + +``` +add(1, 1.5) +``` + +And returns the result **2.5**. + +This example concatenates the specified items: + +``` +add('hello',null) +add('hello','world') +``` + +And returns the results + +- **hello** +- **helloworld** + + + +### addDays + +Add a number of days to a timestamp in an optional locale format. + +``` +addDays('', , ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp which must be standard UTC ISO format
YYYY-MM-DDTHH:mm:ss.fffZ | +| <*days*> | Yes | integer | The positive or negative number of days to add | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp plus the specified number of days | + +*Example 1* + +This example adds **10** days to the specified timestamp: + +``` +addDays('2018-03-15T13:00:00.000Z', 10) +``` + +And returns the result **2018-03-25T00:00:00.000Z**. + +*Example 2* + +This example subtracts five days from the specified timestamp: + +``` +addDays('2018-03-15T00:00:00.000Z', -5) +``` + +And returns the result **2018-03-10T00:00:00.000Z**. + +*Example 3* + +This example adds **1** day to the specified timestamp in the **de-DE** locale: + +``` +addDays('2018-03-15T13:00:00.000Z', 1, '', 'de-dE') +``` + +And returns the result **16.03.18 13:00:00**. + + + +### addHours + +Add a number of hours to a timestamp in an optional locale format. + +``` +addHours('', , ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*hours*> | Yes | integer | The positive or negative number of hours to add | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp plus the specified number of hours | + +*Example 1* + +This example adds **10** hours to the specified timestamp: + +``` +addHours('2018-03-15T00:00:00.000Z', 10) +``` + +And returns the result **2018-03-15T10:00:00.000Z**. + +*Example 2* + +This example subtracts five hours from the specified timestamp: + +``` +addHours('2018-03-15T15:00:00.000Z', -5) +``` + +And returns the result **2018-03-15T10:00:00.000Z**. + +*Example 3* + +This example adds **2** hours to the specified timestamp in the **de-DE** locale: + +``` +addHours('2018-03-15T13:00:00.000Z', 2, '', 'de-DE') +``` + +And returns the result **15.03.18 15:00:00**. + + + +### addMinutes + +Add a number of minutes to a timestamp in an optional locale format. + +``` +addMinutes('', , ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*minutes*> | Yes | integer | The positive or negative number of minutes to add | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp plus the specified number of minutes | + +*Example 1* + +This example adds **10** minutes to the specified timestamp: + +``` +addMinutes('2018-03-15T00:10:00.000Z', 10) +``` + +And returns the result **2018-03-15T00:20:00.000Z**. + +*Example 2* + +This example subtracts five minutes from the specified timestamp: + +``` +addMinutes('2018-03-15T00:20:00.000Z', -5) +``` + +And returns the result **2018-03-15T00:15:00.000Z**. + +*Example 3* + +This example adds **30** minutes to the specified timestamp in the **de-DE** locale: + +``` +addMinutes('2018-03-15T00:00:00.000Z', 30, '', 'de-DE') +``` + +And returns the result **15.03.18 13:30:00**. + + + +### addOrdinal + +Return the ordinal number of the input number. + +``` +addOrdinal() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*>| Yes | integer | The numbers to convert to an ordinal number | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result*> | string | The ordinal number converted from the input number | + +*Example* + +``` +addOrdinal(11) +addOrdinal(12) +addOrdinal(13) +addOrdinal(21) +addOrdinal(22) +addOrdinal(23) +``` + +And respectively returns these results: + +- **11th** +- **12th** +- **13th** +- **21st** +- **22nd** +- **23rd** + + + +### addProperty + +Add a property and its value, or name-value pair, to a JSON object, and return the updated object. If the object already exists at runtime the function throws an error. + +``` +addProperty('', '', value) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object*> | Yes | object | The JSON object where you want to add a property | +|<*property*>| Yes | string | The name of the property to add | +|<*value*>| Yes | any | The value of the property | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-object*> | object | The updated JSON object after adding a new property | + +*Example* + +This example adds the **accountNumber** property to the **customerProfile** object, which is converted to JSON with the [json()](#json) function. The function assigns a value that is generated by the [newGuid()](#newGuid) function, and returns the updated object: + +``` +addProperty(json('customerProfile'), 'accountNumber', newGuid()) +``` + + + +### addSeconds + +Add a number of seconds to a timestamp. + +``` +addSeconds('', , ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*seconds*> | Yes | integer | The positive or negative number of seconds to add | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp plus the specified number of seconds | + +*Example 1* + +This example adds 10 seconds to the specified timestamp: + +``` +addSeconds('2018-03-15T00:00:00.000Z', 10) +``` + +And returns the result **2018-03-15T00:00:10.000Z**. + +*Example 2* + +This example subtracts five seconds to the specified timestamp: + +``` +addSeconds('2018-03-15T00:00:30.000Z', -5) +``` + +And returns the result **2018-03-15T00:00:25.000Z**. + + + +### addToTime + +Add a number of time units to a timestamp in an optional locale format. See also [getFutureTime()](#getFutureTime). + +``` +addToTime('', '', , ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*interval*> | Yes | integer | The number of specified time units to add | +| <*timeUnit*> | Yes | string | The unit of time to use with *interval*. Possible units are "Second", "Minute", "Hour", "Day", "Week", "Month", and "Year". | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp plus the number of specified time units with given format. | + +*Example 1* + +This example adds one day to specified timestamp. + +``` +addToTime('2018-01-01T00:00:00.000Z', 1, 'Day') +``` + +And returns the result **2018-01-02T00:00:00.000Z**. + +*Example 2* + +This example adds two weeks to the specified timestamp. + +``` +addToTime('2018-01-01T00:00:00.000Z', 2, 'Week', 'MM-DD-YY') +``` + +And returns the result in the 'MM-DD-YY' format as **01-15-18**. + + + +### all + +Determine whether all elements of a sequence satisfy a condition. + +``` +all(, , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*sequence*> | Yes | object | A sequence to be evaluated. | +| <*item*> | Yes | string | Refers to the elements to evaluate in the sequence. | +| <*condition*> | Yes | expression | The expression to evaluate the condition. | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| true or false | Boolean | Return `true` if all elements satisfy a condition. Return `false` if at least one doesn't. | + +*Examples* + +These examples determine if all elements of a sequence satisfy a condition: + +``` +all(createArray(1, 'cool'), item, isInteger(item)) +all(createArray(1, 2), item => isInteger(item)) +``` + +And return the following results respectively: + +- **false**, because both items in the sequence aren't integers. +- **true**, because both items in the sequence are integers. + + + +### and + +Check whether all expressions are true. Return `true` if all expressions are true, or return `false` if at least one expression is false. + +``` +and(, , ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*expression1*>, <*expression2*>, ... | Yes | Boolean | The expressions to check | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| true or false | Boolean | Return `true` if all expressions are true. Return `false` if at least one expression is false. | + +*Example 1* + +These examples check whether the specified Boolean values are all true: + +``` +and(true, true) +and(false, true) +and(false, false) +``` + +And respectively returns these results: + +- Both expressions are true, so the functions returns `true`. +- One expression is false, so the functions returns `false`. +- Both expressions are false, so the function returns `false`. + +*Example 2* + +These examples check whether the specified expressions are all true: + +``` +and(equals(1, 1), equals(2, 2)) +and(equals(1, 1), equals(1, 2)) +and(equals(1, 2), equals(1, 3)) +``` + +And respectively returns these results: + +- Both expressions are true, so the functions returns `true`. +- One expression is false, so the functions returns `false`. +- Both expressions are false, so the functions returns `false`. + + + +### any + +Determine whether any elements of a sequence satisfy a condition. + +``` +all(, , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*sequence*> | Yes | object | A sequence to be evaluated. | +| <*item*> | Yes | string | Refers to the elements to evaluate in the sequence. | +| <*condition*> | Yes | expression | The expression to evaluate the condition. | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| true or false | Boolean | Return `true` if all elements satisfy the condition. Return `false` if at least one doesn't. | + +*Examples* + +These examples determine if all elements of a sequence satisfy a condition: + +``` +any(createArray(1, 'cool'), item, isInteger(item)) +any(createArray('first', 'cool'), item => isInteger(item)) +``` + +And return the following results respectively: + +- **true**, because at least one item in the sequence is an integer +- **false**, because neither item in the sequence is an integer. + + + +### average + +Return the number average of a numeric array. + +``` +average() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*numericArray*> | Yes | array of number | The input array to calculate the average | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*average-of-array*> | number | The average value of the given array | + +*Example* + +This example calculates the average of the array in `createArray()`: + +``` +average(createArray(1,2,3)) +``` + +And returns the result **2**. + + + +### base64 + +Return the base64-encoded version of a string or byte array. + +``` +base64('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string or byte array | The input string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*base64-string*> | string | The base64-encoded version of the input string | + +*Example 1* + +This example converts the string **hello** to a base64-encoded string: + +``` +base64('hello') +``` + +And returns the result **"aGVsbG8="**. + +*Example 2* + +This example takes `byteArr`, which equals `new byte[] { 3, 5, 1, 12 }`: + +``` +base64('byteArr') +``` + +And returns the result **"AwUBDA=="**. + + + +### base64ToBinary + +Return the binary array of a base64-encoded string. + +``` +base64ToBinary('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The base64-encoded string to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*binary-for-base64-string*> | byte array | The binary version of the base64-encoded string | + +*Example* + +This example converts the base64-encoded string **AwUBDA==** to a binary string: + +``` +base64ToBinary('AwUBDA==') +``` + +And returns the result **new byte[] { 3, 5, 1, 12 }**. + + + +### base64ToString + +Return the string version of a base64-encoded string, effectively decoding the base64 string. + +``` +base64ToString('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The base64-encoded string to decode | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*decoded-base64-string*> | string | The string version of a base64-encoded string | + +*Example* + +This example converts the base64-encoded string **aGVsbG8=** to a decoded string: + +``` +base64ToString('aGVsbG8=') +``` + +And returns the result **hello**. + + + +### binary + +Return the binary version of a string. + +``` +binary('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The string to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*binary-for-input-value*> | byte array | The binary version of the specified string | + +*Example* + +This example converts the string **hello** to a binary string: + +``` +binary('hello') +``` + +And returns the result **new byte[] { 104, 101, 108, 108, 111 }**. + + + +### bool + +Return the Boolean version of a value. + +``` +bool() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | any | The value to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | The Boolean version of the specified value | + +*Example* + +These examples convert the specified values to Boolean values: + +``` +bool(1) +bool(0) +``` + +And respectively returns these results: + +* `true` +* `false` + + + +### ceiling + +Return the largest integral value less than or equal to the specified number. + +``` +ceiling('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | An input number | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*integer-value*> | integer | The largest integral value greater than or equal to the input number | + +*Example* + +This example returns the largest integral value less than or equal to the number **10.333**: + +``` +ceiling(10.333) +``` + +And returns the integer **11**. + + + +### coalesce + +Return the first non-null value from one or more parameters. Empty strings, empty arrays, and empty objects are not null. + +``` +coalesce(, , ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object**1*>, <*object**2*>, ... | Yes | any (mixed types acceptable) | One or more items to check for null| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*first-non-null-item*> | any | The first item or value that isn't null. If all parameters are null, this function returns null. | + +*Example* + +These examples return the first non-null value from the specified values, or null when all the values are null: + +``` +coalesce(null, true, false) +coalesce(null, 'hello', 'world') +coalesce(null, null, null) +``` + +And respectively return: + +- `true` +- **hello** +- null + + + +### concat + +Combine two or more objects, and return the combined objects in a list or string. + +``` +concat('', '', ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object1*>, <*object2*>, ... | Yes | any | At least two objects to concat. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*object1object2...*> | string or list | The combined string or list. Null values are skipped. | + +Expected return values: + +- If all items are lists, a list will be returned. +- If there exists an item that isn't a list, a string will be returned. +- If a value is null, it's skipped and not concatenated. + +*Example* + +This example combines the strings **Hello** and **World**: + +``` +concat('Hello', 'World') +``` + +And returns the result **HelloWorld**. + +*Example 2* + +This example combines the lists **[1,2]** and **[3,4]**: + +``` +concat([1,2],[3,4]) +``` + +And returns the result **[1,2,3,4]**. + +*Example 3* + +These examples combine objects of different types: + +``` +concat('a', 'b', 1, 2) +concat('a', [1,2]) +``` + +And return the following results respectively: + +- The string **ab12**. +- The object **aSystem.Collections.Generic.List 1[System.Object]**. This is unreadable and best to avoid. + +*Example 4* + +These examples combine objects will `null`: + +``` +concat([1,2], null) +concat('a', 1, null) +``` + +And return the following results respectively: + +- The list **[1,2]**. +- The string **a1**. + + + +### contains + +Check whether a collection has a specific item. Return `true` if the item is found, or return `false` if not found. This function is case-sensitive. + +``` +contains('', '') +contains([], '') +``` + +This function works on the following collection types: + +- A string to find a substring +- An array to find a value +- A dictionary to find a key + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string, array, or dictionary | The collection to check | +| <*value*> | Yes | string, array, or dictionary, respectively | The item to find | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the item is found. Return `false` if not found. | + +*Example 1* + +This example checks the string **hello world** for the substring **world**: + +``` +contains('hello world', 'world') +``` + +And returns the result `true`. + +*Example 2* + +This example checks the string **hello world** for the substring **universe**: + +``` +contains('hello world', 'universe') +``` + +And returns the result `false`. + + + +### count + +Return the number of items in a collection. + +``` +count('') +count([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection with the items to count | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*length-or-count*> | integer | The number of items in the collection | + +*Examples*: + +These examples count the number of items in these collections: + +``` +count('abcd') +count(createArray(0, 1, 2, 3)) +``` + +And both return the result **4**. + + + +### countWord + +Return the number of words in a string + +``` +countWord('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to count | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*count*> | integer | The number of words in the string | + +*Example* + +This example counts the number of words in the string **hello world**: + +``` +countWord("hello word") +``` + +And it returns the result **2**. + + + +### convertFromUTC + +Convert a timestamp in an optional locale format from Universal Time Coordinated (UTC) to a target time zone. + +``` +convertFromUTC('', '', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*destinationTimeZone*> | Yes | string | The name of the target time zone. Supports Windows and IANA time zones. | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is the ["o" format](/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip), yyyy-MM-ddTHH:mm:ss.fffffffK, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*converted-timestamp*> | string | The timestamp converted to the target time zone | + +*Examples*: + +These examples convert from UTC to Pacific Standard Time: + +``` +convertFromUTC('2018-02-02T02:00:00.000Z', 'Pacific Standard Time', 'MM-DD-YY') +convertFromUTC('2018-02-02T02:00:00.000Z', 'Pacific Standard Time') +``` + +And respectively return these results: + +- **02-01-18** +- **2018-01-01T18:00:00.0000000** + +*Example 2* + +This example converts a timestamp in the **en-US** locale from UTC to Pacific Standard Time: + +``` +convertFromUTC('2018-01-02T02:00:00.000Z', 'Pacific Standard Time', 'D', 'en-US') +``` + +And returns the result **Monday, January 1, 2018**. + + + +### convertToUTC + +Convert a timestamp in an optional locale format to Universal Time Coordinated (UTC) from the source time zone. + +``` +convertToUTC('', '', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*sourceTimeZone*> | Yes | string | The name of the target time zone. Supports Windows and IANA time zones. | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*converted-timestamp*> | string | The timestamp converted to the target time zone | + +*Example* + +This example converts a timestamp to UTC from Pacific Standard Time + +``` +convertToUTC('01/01/2018 00:00:00', 'Pacific Standard Time') +``` + +And returns the result **2018-01-01T08:00:00.000Z**. + +*Example 2* + +This example converts a timestamp in the **de-DE** locale to UTC from Pacific Standard Time: + +``` +convertToUTC('01/01/2018 00:00:00', 'Pacific Standard Time', '', 'de-DE') +``` + +And returns the result **01.01.18 08:00:00**. + + + +### createArray + +Return an array from multiple inputs. + +``` +createArray('', '', ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object1*>, <*object2*>, ... | Yes | any, but not mixed | At least two items to create the array | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| [<*object1*>, <*object2*>, ...] | array | The array created from all the input items | + +*Example* + +This example creates an array from the following inputs: + +``` +createArray('h', 'e', 'l', 'l', 'o') +``` + +And returns the result **[h ,e, l, l, o]**. + + + +### dataUri + +Return a data uniform resource identifier (URI) of a string. + +``` +dataUri('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*>| Yes | string | The string to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| [<*date-uri*>] | string | The data URI for the input string | + +*Example* + +``` +dataUri('hello') +``` + +Returns the result **data:text/plain;charset=utf-8;base64,aGVsbG8=**. + + + +### dataUriToBinary + +Return the binary version of a data uniform resource identifier (URI). + +``` +dataUriToBinary('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*>| Yes | string | The data URI to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| [<*binary-for-data-uri*>] | byte array | The binary version of the data URI | + +*Example* + +This example creates a binary version for the following data URI: + +``` +dataUriToBinary('aGVsbG8=') +``` + +And returns the result **new byte[] { 97, 71, 86, 115, 98, 71, 56, 61 }**. + + + +### dataUriToString + +Return the string version of a data uniform resource identifier (URI). + +``` +dataUriToString('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*>| Yes | string | The data URI to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| [<*string-for-data-uri*>] | string | The string version of the data URI | + +*Example* + +This example creates a string from the following data URI: + +``` +dataUriToString('data:text/plain;charset=utf-8;base64,aGVsbG8=') +``` + +And returns the result **hello**. + + + +### date + +Return the date of a specified timestamp in **m/dd/yyyy** format. + +``` +date('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*date*> | string | The date of the specified timestamp | + +``` +date('2018-03-15T13:00:00.000Z') +``` + +Returns the result **3-15-2018**. + + + +### dateReadBack + +Uses the date-time library to provide a date readback. + +``` +dateReadBack('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*currentDate*> | Yes | string | The string that contains the current date | +| <*targetDate*> | Yes | string | The string that contains the target date | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*date-readback*> | string | The readback between current date and the target date | + +*Example 1* + +``` +dateReadBack('2018-03-15T13:00:00.000Z', '2018-03-16T13:00:00.000Z') +``` + +Returns the result **tomorrow**. + + + +### dateTimeDiff + +Return the difference in ticks between two timestamps. + +``` +dateTimeDiff('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp1*> | Yes | string | The first timestamp string to compare | +| <*timestamp2*> | Yes | string | The second timestamp string to compare | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*ticks*> | number | The difference in ticks between two timestamps | + +*Example 1* + +This example returns the difference in ticks between two timestamps: + +``` +dateTimeDiff('2019-01-01T08:00:00.000Z','2018-01-01T08:00:00.000Z') +``` + +And returns the number **315360000000000**. + +*Example 2* + +This example returns the difference in ticks between two timestamps: + +``` +dateTimeDiff('2018-01-01T08:00:00.000Z', '2019-01-01T08:00:00.000Z') +``` + +Returns the result **-315360000000000**. The value is a negative number. + + + +### dayOfMonth + +Return the day of the month from a timestamp. + +``` +dayOfMonth('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*day-of-month*> | integer | The day of the month from the specified timestamp | + +*Example* + +This example returns the number for the day of the month from the following timestamp: + +``` +dayOfMonth('2018-03-15T13:27:36Z') +``` + +And returns the result **15**. + + + +### dayOfWeek + +Return the day of the week from a timestamp. + +``` +dayOfWeek('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*day-of-week*> | integer | The day of the week from the specified timestamp. Sunday is 0, Monday is 1, and so forth. | + +*Example* + +This example returns the number for the day of the week from the following timestamp: + +``` +dayOfWeek('2018-03-15T13:27:36Z') +``` + +And returns the result **3**. + + + +### dayOfYear + +Return the day of the year from a timestamp. + +``` +dayOfYear('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*day-of-year*> | integer | The day of the year from the specified timestamp | + +*Example* + +This example returns the number of the day of the year from the following timestamp: + +``` +dayOfYear('2018-03-15T13:27:36Z') +``` + +And returns the result **74**. + + + +### div + +Return the integer result from dividing two numbers. To return the remainder see [mod()](#mod). + +``` +div(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*dividend*> | Yes | number | The number to divide by the *divisor* | +| <*divisor*> | Yes | number | The number that divides the *dividend*. Can't be 0. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*quotient-result*> | number | The result from dividing the first number by the second number | + +*Example* + +Both examples divide the first number by the second number: + +``` +div(10, 5) +div(11, 5) +``` + +And return the result **2**. + +There exists some gap between Javascript and .NET SDK. For example, the following expression will return different results in Javascript and .NET SDK: + +If one of the parameters is a float, the result will also be a FLOAT with .NET SDK. + +*Example* + +``` +div(11.2, 2) +``` + +Returns the result **5.6**. + +If one of the parameters is a float, the result will be an INT with Javascript SDK. + +*Example* + +``` +div(11.2, 2) +``` + +Returns the result **5**. + +The workaround for Javascript to keep a certain number of decimal places in results is to use such expression. For example, to keep 3 decimal places: + +``` +float(concat(string(div(a, b)),'.',string(mod(div(a*1000, b), 1000)))) +``` + + + +### empty + +Check whether an instance is empty. Return `true` if the input is empty. +Empty means: + +- input is null or undefined +- input is a null or empty string +- input is zero size collection +- input is an object with no property. + +``` +empty('') +empty([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*instance*> | Yes | any | The instance to check | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` when the instance is empty.| + +*Example* + +These examples check whether the specified instance is empty: + +``` +empty('') +empty('abc') +empty([1]) +empty(null) +``` + +And return these results respectively: + +- Passes an empty string, so the function returns `true`. +- Passes the string **abc**, so the function returns `false`. +- Passes the collection with one item, so the function returns `false`. +- Passes the null object, so the function returns `true`. + + + +### endsWith + +Check whether a string ends with a specific substring. Return `true` if the substring is found, or return `false` if not found. This function is case-insensitive. + +``` +endsWith('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to check | +| <*searchText*> | Yes | string | The ending substring to find | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` when the ending substring is found. Return `false` if not found | + +*Example 1* + +This example checks whether the **hello world** string ends with the string **world**: + +``` +endsWith('hello world', 'world') +``` + +And it returns the result `true`. + +*Example 2* + +This example checks whether the **hello world** string ends with the string **universe**: + +``` +endsWith('hello world', 'universe') +``` + +And it returns the result `false`. + + + +### EOL + +Return the end of line (EOL) sequence text. + +``` +EOL() +``` + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*IsOSPlatform*>| string | Return **\r\n** in Windows and **\n** in Mac and Linux. | + +*Example* + +This example checks the end of the line sequence text: + +``` +EOL() +``` + +And returns the following strings: + +- Windows: **\r\n** +- Mac or Linux: **\n** + + + +### equals + +Check whether both values, expressions, or objects are equivalent. Return `true` if both are equivalent, or return `false` if they're not equivalent. + +``` +equals('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object1*>, <*object2*> | Yes | any | The values, expressions, or objects to compare | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` when both are equivalent. Return `false` if not equivalent. | + +*Example* + +These examples check whether the specified inputs are equivalent: + +``` +equals(true, 1) +equals('abc', 'abcd') +``` + +And returns these results respectively: + +- Both values are equivalent, so the function returns `true`. +- Both values aren't equivalent, so the function returns `false`. + + + +### exists + +Evaluates an expression for truthiness. + +``` +exists(expression) +``` + +| Parameter | Required | Type | Description | +|-----------|----------|------|-------------| +| expression | Yes | expression | Expression to evaluate for truthiness | + +| Return value | Type | Description | +|--------------|------|-------------| +| <*true or false*> | Boolean | Result of evaluating the expression | + +*Example* + +These example evaluate the truthiness of `foo = {"bar":"value"}`: + +``` +exists(foo.bar) +exists(foo.bar2) +``` + +And return these results respectively: + +- `true` +- `false` + + + +### exp + +Return exponentiation of one number to another. + +``` +exp(realNumber, exponentNumber) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| realNumber | Yes | number | Real number to compute exponent of | +| exponentNumber | Yes | number | Exponent number | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result-exp*> | number | The result from computing exponent of `realNumber` | + +*Example* + +This example computes the exponent: + +``` +exp(2, 2) +``` + +And returns the result **4**. + + + +### first + +Return the first item from a string or array. + +``` +first('') +first([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection in which to find the first item | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*first-collection-item*> | any | The first item in the collection | + +*Example* + +These examples find the first item in the following collections: + +``` +first('hello') +first(createArray(0, 1, 2)) +``` + +And return these results respectively: + +- **h** +- **0** + + + +### flatten + +Flatten an array into non-array values. You can optionally set the maximum depth to flatten to. + +``` +flatten([], '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | array | The collection to flatten | +| <*depth*> | No | number | Maximum depth to flatten. Default is infinity. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | New collection whose elements have been flattened to a non-array to the specified depth | + +*Example 1* + +THis example flattens the following array: + +``` +flatten(createArray(1, createArray(2), createArray(createArray(3, 4), createArray(5, 6))) +``` + +And returns the result **[1, 2, 3, 4, 5, 6]**. + +*Example 2* + +This example flattens the array to a depth of **1**: + +``` +flatten(createArray(1, createArray(2), createArray(createArray(3, 4), createArray(5, 6)), 1) +``` + +And returns the result **[1, 2, [3, 4], [5, 6]]**. + + + +### float + +Convert the string version of a floating-point number to a floating-point number. You can use this function only when passing custom parameters to an app, such as a logic app. An exception will be thrown if the string can't be converted to a float. + +``` +float('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The string that has a valid floating-point number to convert to | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*float-value*> | float | The floating-point number of the specified string | + +*Example* + +This example converts the float version of a string: + +``` +float('10.333') +``` + +And returns the float **10.333**. + + + +### floor + +Return the largest integral value less than or equal to the specified number. + +``` +floor('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | An input number | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*integer-value*> | integer | The largest integral value less than or equal to the input number | + +*Example* + +This example calculates the floor value of the number **10.333**: + +``` +floor(10.333) +``` + +And returns the integer **10**. + + + +### foreach + +Operate on each element and return the new collection. + +``` +foreach([], , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection/instance*> | Yes | array or object | The collection with the items | +| <*iteratorName*> | Yes | iterator name | The key item of arrow function | +| <*function*> | Yes | expression | Function that contains `iteratorName` | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | The new collection in which each element has been evaluated by the function | + +*Example 1* + +This example generates a new collection: + +``` +foreach(createArray(0, 1, 2, 3), x, x + 1) +``` + +And returns the result **[1, 2, 3, 4]**. + +*Example 2* + +These examples generate a new collection: + +``` +foreach(json("{'name': 'jack', 'age': '15'}"), x, concat(x.key, ':', x.value)) +foreach(json("{'name': 'jack', 'age': '15'}"), x=> concat(x.key, ':', x.value)) +``` + +And return the result **['name:jack', 'age:15']**. Note that the second expression is a *lambda expression*, which some find more readable. + + + +### formatDateTime + +Return a timestamp in an optional locale format. + +``` +formatDateTime('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*reformatted-timestamp*> | string | The updated timestamp in the specified format | + +*Example 1* + +This example converts a timestamp to the specified format: + +``` +formatDateTime('03/15/2018 12:00:00', 'yyyy-MM-ddTHH:mm:ss') +``` + +And returns the result **2018-03-15T12:00:00**. + +*Example 2* + +This example converts a timestamp in the **de-DE** locale: + +``` +formatDateTime('2018-03-15', '', 'de-DE') +``` + +And returns the result **15.03.18 00:00:00**. + + + +### formatEpoch + +Return a timestamp in an optional locale format in the specified format from UNIX time (also know as Epoch time, POSIX time, UNIX Epoch time). + +``` +formatEpoch('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*epoch*> | Yes | number | The epoch number | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*reformatted-timestamp*> | string | The updated timestamp in the specified format | + +*Example* + +This example converts a Unix timestamp to the specified format: + +``` +formatEpoch(1521118800, 'yyyy-MM-ddTHH:mm:ss.fffZ)' +``` + +And returns the result **2018-03-15T12:00:00.000Z**. + +*Example* + +This example converts a Unix timestamp in the **de-DE** locale: + +``` +formatEpoch(1521118800, '', 'de-DE') +``` + +And returns the result **15.03.18 13:00:00**. + + + +### formatNumber + +Format a value to the specified number of fractional digits and an optional specified locale. + +``` +formatNumber('', '', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | An input number | +| <*precision-digits*> | Yes | integer | A specified number of fractional digits| +| <*locale*> | No| string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*return-value*> | number | The return value of the input formatted at a specified number of fractional digits and a specified locale | + +*Example 1* + +This example formats the number **10.333** to **2** fractional digits: + +``` +formatNumber(10.333, 2) +``` + +And returns the string **10.33**. + +*Example 2* + +These examples format numbers to a specified number of digits in the **en-US** locale: + +``` +formatNumber(12.123, 2, 'en-US') +formatNumber(1.551, 2, 'en-US') +formatNumber(12.123, 4, 'en-US') +``` + +And return the following results respectively: + +- **12.12** +- **1.55** +- **12.1230** + + + +### formatTicks + +Return a timestamp in an optional locale format in the specified format from ticks. + +``` +formatTicks('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*epoch*> | Yes | number (or bigint in JavaScript)| The ticks number | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*reformatted-timestamp*> | string | The updated timestamp in the specified format | + +*Example 1* + +This example converts ticks to the specified format: + +``` +formatTicks(637243624200000000, 'yyyy-MM-ddTHH:mm:ss.fffZ') +``` + +And returns the result **2020-05-06T11:47:00.000Z**. + +*Example 2* + +This example converts ticks to the specified format in the **de-DE** locale: + +``` +formatTicks(637243624200000000, '', 'de-DE') +``` + +And returns the result **06.05.20 11:47:00**. + + + +### getFutureTime + +Return the current timestamp in an optional locale format plus the specified time units. + +``` +getFutureTime(, , ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*interval*> | Yes | integer | The number of specific time units to add | +| <*timeUnit*> | Yes | string | The unit of time to use with *interval*. Possible units are "Second", "Minute", "Hour", "Day", "Week", "Month", and "Year".| +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The current timestamp plus the specified number of time units | + +*Example 1* + +Suppose the current timestamp is **2019-03-01T00:00:00.000Z**. The example below adds five days to that timestamp: + +``` +getFutureTime(2, 'Week') +``` + +And returns the result **2019-03-15T00:00:00.000Z**. + +*Example 2* + +Suppose the current timestamp is **2018-03-01T00:00:00.000Z**. The example below adds five days to the timestamp and converts the result to **MM-DD-YY** format: + +``` +getFutureTime(5, 'Day', 'MM-DD-YY') +``` + +And returns the result **03-06-18**. + +*Example 3* + +Suppose the current timestamp is **2020-05-01T00:00:00.000Z** and the locale is **de-DE**. The example below adds **1** day to the timestamp: + +``` +getFutureTime(1,'Day', '', 'de-DE') +``` + +And returns the result **02.05.20 00:00:00**. + + + +### getNextViableDate + +Return the next viable date of a Timex expression based on the current date and an optionally specified timezone. + +``` +getNextViableDate(, ?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timexString*> | Yes | string | The Timex string of the date to evaluate. | +| <*timezone*> | No | string | The optional timezone. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*nextViableTime*> | string | The next viable date. | + +*Examples* + +Say the date is **2020-06-12** and current time is **15:42:21**. + +These examples evaluate the Timex string for the next viable date based on the above date and time: + +``` +getPreviousViableDate("XXXX-12-20", "America/Los_Angeles") +getPreviousViableDate("XXXX-02-29") +``` + +And return the following strings respectively: + +- **2020-12-20** +- **2024-02-29** + + + +### getNextViableTime + +Return the next viable time of a Timex expression based on the current time and an optionally specified timezone. + +``` +getNextViableTime(, ?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timexString*> | Yes | string | The Timex string of the time to evaluate. | +| <*timezone*> | No | string | The optional timezone. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*nextViableTime*> | string | The next viable time. | + +*Examples* + +Say the date is **2020-06-12** and current time is **15:42:21**. + +These examples evaluate a Timex string for the next viable time based on the above date and time: + +``` +getNextViableTime("TXX:12:14", "Asia/Tokyo") +getNextViableTime("TXX:52:14") +``` + +And return the following strings respectively: + +- **T16:12:14** +- **T15:52:14** + + + +### getPastTime + +Return the current timestamp minus the specified time units. + +``` +getPastTime(, , ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*interval*> | Yes | integer | The number of specific time units to subtract | +| <*timeUnit*> | Yes | string | The unit of time to use with *interval*. Possible units are "Second", "Minute", "Hour", "Day", "Week", "Month", and "Year". | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The current timestamp minus the specified number of time units | + +*Example 1* + +Suppose the current timestamp is **2018-02-01T00:00:00.000Z**. This example subtracts five days from that timestamp: + +``` +getPastTime(5, 'Day') +``` + +And returns the result **2019-01-27T00:00:00.000Z**. + +*Example 2* + +Suppose the current timestamp is **2018-03-01T00:00:00.000Z**. This example subtracts five days to the timestamp in the **MM-DD-YY** format: + +``` +getPastTime(5, 'Day', 'MM-DD-YY') +``` + +And returns the result **02-26-18**. + +*Example 3* + +Suppose the current timestamp is **2020-05-01T00:00:00.000Z** and the locale is **de-DE**. The example below subtracts **1** day from the timestamp: + +``` +getPastTime(1,'Day', '', 'de-DE') +``` + +And returns the result **31.04.20 00:00:00**. + + + +### getPreviousViableDate + +Return the previous viable date of a Timex expression based on the current date and an optionally specified timezone. + +``` +getPreviousViableDate(, ?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timexString*> | Yes | string | The Timex string of the date to evaluate. | +| <*timezone*> | No | string | The optional timezone. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*previousViableDate*> | string | The previous viable date. | + +*Examples* + +Say the date is **2020-06-12** and current time is **15:42:21**. + +These examples evaluate a Timex string for the previous viable date based on the above date and time: + +``` +getPreviousViableDate("XXXX-12-20", "Eastern Standard Time") +getPreviousViableDate("XXXX-02-29") +``` + +And return the following strings respectively: + +- **2019-12-20** +- **2020-02-29** + + + +### getPreviousViableTime + +Return the previous viable time of a Timex expression based on the current date and an optionally specified timezone. + +``` +getPreviousViableTime(, ?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timexString*> | Yes | string | The Timex string of the time to evaluate. | +| <*timezone*> | No | string | The optional timezone. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*previousViableTime*> | string | The previous viable time. | + +*Examples* + +Say the date is **2020-06-12** and current time is **15:42:21**. + +These examples evaluate a Timex string for the previous viable time based on the above date and time: + +``` +getPreviousViableTime("TXX:52:14") +getPreviousViableTime("TXX:12:14", 'Europe/London') +``` + +And return the following strings respectively: + +- **T14:52:14** +- **T15:12:14** + + + +### getProperty + +Return the value of a specified property or the root property from a JSON object. + +#### Return the value of a specified property + +``` +getProperty(, '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*JSONObject*> | Yes | object | The JSON object containing the property and values. | +| <*propertyName*> | No | string | The name of the optional property to access values from.| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| value | string | The value of the specified property in the JSON object. | + +*Example* + +Say you have the following JSON object: + +```json +{ + "a:b" : "a:b value", + "c": + { + "d": "d key" + } +} +``` + +These example retrieve a specified property from the above JSON object: + +``` +getProperty({"a:b": "value"}, 'a:b') +getProperty(c, 'd') +``` + +And return the following strings respectively: + +- **a:b value** +- **d key** + +#### Return the root property + +``` +getProperty('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*propertyName*> | Yes | string | The name of the optional property to access values from the root memory scope. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| value | string | The value of the root property in a JSON object. | + +*Example* + +Say you have the following JSON object: + +```json +{ + "a:b" : "a:b value", + "c": + { + "d": "d key" + } +} +``` + +This example retrieves the root property from the above JSON object: + +``` +getProperty("a:b") +``` + +And returns the string **a:b value**. + + + +### getTimeOfDay + +Returns time of day for a given timestamp. + +``` +getTimeOfDay('') +``` + +Time returned is one of the following strings: + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the specified timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*time-of-day*> | string | The time of day for the specified timestamp| + +Listed below are the strings associated with the time of day: + +|Time of day | Timestamp | +|---|---| +| midnight | 12AM | +| morning | 12:01AM – 11:59PM | +| afternoon | 12PM | +| evening | 06:00PM – 10:00PM | +| night | 10:01PM – 11:59PM | + +*Example* + +``` +getTimeOfDay('2018-03-15T08:00:00.000Z') +``` + +Returns the result **morning**. + + + +### greater + +Check whether the first value is greater than the second value. Return `true` if the first value is more, or return `false` if less. + +``` +greater(, ) +greater('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | integer, float, or string | The first value to check whether greater than the second value | +| <*compareTo*> | Yes | integer, float, or string, respectively | The comparison value | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the first value is greater than the second value. Return `false` if the first value is equal to or less than the second value. | + +*Example* + +These examples check whether the first value is greater than the second value: + +``` +greater(10, 5) +greater('apple', 'banana') +``` + +And return the following results respectively: + +- `true` +- `false` + + + +### greaterOrEquals + +Check whether the first value is greater than or equal to the second value. Return `true` when the first value is greater or equal, or return `false` if the first value is less. + +``` +greaterOrEquals(, ) +greaterOrEquals('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | integer, float, or string | The first value to check whether greater than or equal to the second value | +| <*compareTo*> | Yes | integer, float, or string, respectively | The comparison value | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the first value is greater than or equal to the second value. Return `false` if the first value is less than the second value. | + +*Example* + +These examples check whether the first value is greater or equal than the second value: + +``` +greaterOrEquals(5, 5) +greaterOrEquals('apple', 'banana') +``` + +And return the following results respectively: + +- `true` +- `false` + + + +### if + +Check whether an expression is true or false. Based on the result, return a specified value. + +``` +if(, , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*expression*> | Yes | Boolean | The expression to check | +| <*valueIfTrue*> | Yes | any | The value to return if the expression is true | +| <*valueIfFalse*> | Yes | any | The value to return if the expression is false | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*specified-return-value*> | any | The specified value that returns based on whether the expression is `true` or `false` | + +*Example* + +This example evaluates whether `equals(1,1)` is true: + +``` +if(equals(1, 1), 'yes', 'no') +``` + +And returns **yes** because the specified expression returns `true`. Otherwise, the example returns **no**. + + + +### indexOf + +Return the starting position or index value of a substring. This function is case-insensitive, and indexes start with the number 0. + +``` +indexOf('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string or array | The string that has the substring to find | +| <*searchText*> | Yes | string | The substring to find | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*index-value*> | integer | The starting position or index value of the specified substring. +If the string isn't found, return the number -1. | + +*Example 1* + +This example finds the starting index value of the substring **world** in the string **hello world**: + +``` +indexOf('hello world', 'world') +``` + +And returns the result **6**. + +*Example 2* + +This example finds the starting index value of the substring **def** in the array **['abc', 'def', 'ghi']**: +``` +indexOf(createArray('abc', 'def', 'ghi'), 'def') +``` + +And returns the result **1**. + + + +### indicesAndValues + +Turn an array or object into an array of objects with index (current index) and value properties. For arrays, the index is the position in the array. For objects, it's the key for the value. + +``` +indicesAndValues('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection or object*> | Yes | array or object | Original array or object | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*collection*> | array | New array. Each item has two properties: the index with the position in an array or the key for an object, and the corresponding value. | + +*Example 1* + +Say you have a list **{ items: ["zero", "one", "two"] }**. The following function takes that list: + +``` +indicesAndValues(items) +``` + +And returns a new list: + +``` +[ + { + index: 0, + value: 'zero' + }, + { + index: 1, + value: 'one' + }, + { + index: 2, + value: 'two' + } +] +``` + +*Example 2* + +Say you have a list **{ items: ["zero", "one", "two"] }**. The following function takes that list: + +``` +where(indicesAndValues(items), elt, elt.index >= 1) +``` + +And returns a new list: +``` +[ + { + index: 1, + value: 'one' + }, + { + index: 2, + value: 'two' + } +] +``` + +*Example 3* + +Say you have a list **{ items: ["zero", "one", "two"] }**. The following function takes that list: + +``` +join(foreach(indicesAndValues(items), item, item.value), ',') +``` + +And returns the result **zero,one,two**. This expression has the same effect as [join(items, ',')](#join). + +*Example 4* + +Say you have an object **{ user: {name: 'jack', age: 20} }**. The following function takes that object: + +``` +indicesAndValues(user) +``` + +And returns a new object: +``` +[ + { + index: 'name', + value: 'jack' + }, + { + index: 'age', + value: 20 + } +] +``` + + + +### int + +Return the integer version of a string. An exception will be thrown if the string can't be converted to an integer. + +``` +int('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The string to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*integer-result*> | integer | The integer version of the specified string | + +*Example* + +This example creates an integer version for the string **10**: + +``` +int('10') +``` + +And returns the result as the integer **10**. + + + +### intersection + +Return a collection that has only the common items across the specified collections. To appear in the result, an item must appear in all the collections passed to this function. If one or more items have the same name, the last item with that name appears in the result. + +``` +intersection([], [], ...) +intersection('', '', ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection1*>, <*collection2*> | Yes | array or object, but not both | The collections from which you want only the common items | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*common-items*> | array or object, respectively | A collection that has only the common items across the specified collections | + +*Example* + +This example finds the common items across the following arrays: + +``` +intersection(createArray(1, 2, 3), createArray(101, 2, 1, 10), createArray(6, 8, 1, 2)) +``` + +And returns an array with only the items **[1, 2]**. + + + +### isArray + +Return `true` if a given input is an array. + +``` +isArray('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is an array, or return `false` if it's not an array. | + +*Examples* + +The following examples check if the input is an array: + +``` +isArray('hello') +isArray(createArray('hello', 'world')) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input is an array, so the function returns `true`. + + + +### isBoolean + +Return `true` if a given input is a Boolean. + +``` +isBoolean('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is a Boolean, or return `false` if it not a Boolean. | + +*Examples* + +The following examples check if the input is a Boolean: + +``` +isBoolean('hello') +isBoolean(32 > 16) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input is a Boolean, so the function returns `true`. + + +### isDate + +Return `true` if a given TimexProperty or Timex expression refers to a valid date. Valid dates contain the month and dayOfMonth, or contain the dayOfWeek. + +``` +isDate('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object or a Timex expression string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the input refers to a valid date, or return `false` if the date is invalid. | + +*Examples* + +These examples check if the following inputs are valid dates: + +``` +isDate('2020-12') +isDate('xxxx-12-21') +``` + +And return the following results: +- `false` +- `true` + + + +### isDateRange + +Return `true` if a given TimexProperty or Timex expression refers to a valid date range. + +``` +isDateRange('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if given input refers to a valid date range, or return `false` if it's not a valid date range. | + +*Examples* + +These examples check if the following input is a valid date range: + +``` +isDateRange('PT30M') +isDateRange('2012-02') +``` + +And return the following results: + +- `false` +- `true` + + + +### isDateTime + +Return `true` if a given input is a UTC ISO format (**YYYY-MM-DDTHH:mm:ss.fffZ**) timestamp string. + +``` +isDateTime('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is a UTC ISO format timestamp string, or return `false` if it's not a UTC ISO format timestamp string. | + +*Examples* + +The following examples check if the input is a UTC ISO format string: + +``` +isDateTime('hello world!') +isDateTime('2019-03-01T00:00:00.000Z') +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input is a UTC ISO format string, so the function returns `true`. + + + +### isDefinite + +Return `true` if a given TimexProperty or Timex expression refers to a valid date. Valid dates contain the year, month and dayOfMonth. + +``` +isDefinite('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the given input refers to a valid full date, or return `false` if it doesn't refer to a valid full date. | + +*Examples* + +Suppose there is a TimexProperty object **validFullDate = new TimexProperty("2020-02-20")** and the `Now` property is set to `true`. The following examples check if the object refers a valid full date: + +``` +isDefinite('xxxx-12-21') +isDefinite(validFullDate) +``` + +And return the following results respectively: + +- `false` +- `true` + + + +### isDuration + +Return `true` if a given TimexProperty or Timex expression refers to a valid duration. + +``` +isDuration('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the input refers to a valid duration, or return `false` if the input doesn't refer to a valid duration. | + +*Examples* + +The examples below check if the following input refers to a valid duration: + +``` +isDuration('PT30M') +isDuration('2012-02') +``` + +And return the following results respectively: + +- `true` +- `false` + + + +### isFloat + +Return `true` if a given input is a floating-point number. Due to the alignment between C#and JavaScript, a number with an non-zero residue of its modulo 1 will be treated as a floating-point number. + +``` +isFloat('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is a floating-point number, or return `false` if the input isn't a floating-point number. | + +*Examples* + +The following examples check if the input is a floating-point number: + +``` +isFloat('hello world!') +isFloat(1.0) +isFloat(12.01) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input has a modulo that equals 0, so the function returns `false`. +- The input is a floating-point number, so the function returns `true`. + + + +### isInteger + +Return `true` if a given input is an integer number. Due to the alignment between C# and JavaScript, a number with an zero residue of its modulo 1 will be treated as an integer number. + +``` +isInteger('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Is the input is an integer number | + +*Examples* + +The following examples check if the input is an integer: + +``` +isInteger('hello world!') +isInteger(1.0) +isInteger(12) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input has a modulo that equals 0, so the function returns `true`. +- The input is an integer, so the function returns `true`. + + + +### isMatch + +Return `true` if a given string is matches a specified regular expression pattern. + +``` +isMatch('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*target**string*> | Yes | string | The string to be matched | +| <*pattern*> | Yes | string | A regular expression pattern | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given string is matches a common regular expression pattern, or return `false` if it doesn't match the pattern. | + +*Examples* + +The following examples check if the input matches the specified regular expression pattern: + +``` +isMatch('ab', '^[a-z]{1,2}$') +isMatch('FUTURE', '(?i)fortune|future') +isMatch('12abc34', '([0-9]+)([a-z]+)([0-9]+)') +isMatch('abacaxc', 'ab.*?c') +``` + +And return the same result `true`. + + + +### isObject + +Return `true` if a given input is a complex object or return `false` if it's a primitive object. Primitive objects include strings, numbers, and Booleans; complex types, like classes, contain properties. + +``` +isObject('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is a complex object, or return `false` if it's a primitive object. | + +*Examples* + +The following examples check if the given input is an object: + +``` +isObject('hello world!') +isObject({userName: "Sam"}) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `false`. +- The input is an object, so the function returns `true`. + + + +### isPresent + +Return `true` if a given TimexProperty or Timex expression refers to the present. + +``` +isPresent('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the input refers to the present, or return false if it doesn't refer to the present. | + +*Examples* +Suppose we have an TimexProperty object **validNow = new TimexProperty() { Now = true }** and set the `Now` property to `true`. The examples below check if the following input refers to the present: + +``` +isPresent('PT30M') +isPresent(validNow) +``` + +And return the following results respectively: + +- `false` +- `true` + + + +### isString + +Return `true` if a given input is a string. + +``` +isString('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | any | The input to be tested | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Boolean-result*> | Boolean | Return `true` if a given input is a string, or return `false` if not a string. | + +*Examples* + +The following examples check if the given input is a string: + +``` +isString('hello world!') +isString(3.14) +``` + +And return the following results respectively: + +- The input is a string, so the function returns `true`. +- The input is a float, so the function returns `false`. + + + +### isTime + +Return `true` if a given TimexProperty or Timex expression refers to a valid time. Valid time contains hours, minutes and seconds. + +``` +isTime('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the input refers to a valid time, or return `false` if it doesn't refer to a valid time.. | + +*Examples* + +These examples check if the following input refers to a valid time: + +``` +isTime('PT30M') +isTime('2012-02-21T12:30:45') +``` + +And return the following results respectively: + +- `false` +- `true` + + + +### isTimeRange + +Return `true` if a given TimexProperty or Timex expression refers to a valid time range Valid time ranges contain partOfDay. + +``` +isTime('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*input*> | Yes | object or string | The input TimexProperty object a Timex expression string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*boolean-result*> | Boolean | Return `true` if the input refers to a valid time range, or return `false` if it doesn't refer to a valid time range. | + +*Examples* + +Suppose we have an TimexProperty object **validTimeRange = new TimexProperty() { PartOfDay = "morning" }** and set the `Now` property to `true`. These examples check if the following inputs are valid time ranges: + +``` +isTimeRange('PT30M') +isTimeRange(validTimeRange) +``` + +And return the following results respectively: + +- `false` +- `true` + + + +### join + +Return a string that has all the items from an array, with each character separated by a *delimiter*. + +``` +join([], '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | array | The array that has the items to join | +| <*delimiter*> | Yes | string | The separator that appears between each character in the resulting string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*char1*><*delimiter*><*char2*><*delimiter*>... | string | The resulting string created from all the items in the specified array | + +*Example* + +This example creates a string from all the items in this array with the specified character **.** as the delimiter: + +``` +join(createArray('a', 'b', 'c'), '.') +``` + +And returns the result **a.b.c**. + + + +### jPath + +Check JSON or a JSON string for nodes or values that match a path expression, and return the matching nodes. + +``` +jPath(, '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*json*> | Yes | any | The JSON object or string to search for nodes or values that match the path expression value | +| <*path*> | Yes | any | The path expression used to find matching JSON nodes or values | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +|[ <*json-node*>] | array | A list of JSON nodes or values that matches the specified path expression | + +*C# Example* + +Say you have the following JSON: + +```json +{ + "Stores": [ + "Lambton Quay", + "Willis Street" + ], + "Manufacturers": [ + { + "Name": "Acme Co", + "Products": [ + { + "Name": "Anvil", + "Price": 50 + } + ] + }, + { + "Name": "Contoso", + "Products": [ + { + "Name": "Elbow Grease", + "Price": 99.95 + }, + { + "Name": "Headlight Fluid", + "Price": 4 + } + ] + } + ] +} +``` + +The path expression is **$..Products[?(@.Price >= 50)].Name** + +``` +jPath(jsonStr, path) +``` + +And it returns the result **["Anvil", "Elbow Grease"]**. + +*JavaScript Example* + +Say you have the following JSON: + +```json +{ + "automobiles": [ + { + "maker": "Nissan", + "model": "Teana", + "year": 2011 + }, + { + "maker": "Honda", + "model": "Jazz", + "year": 2010 + }, + { + "maker": "Honda", + "model": "Civic", + "year": 2007 + }, + { + "maker": "Toyota", + "model": "Yaris", + "year": 2008 + }, + { + "maker": "Honda", + "model": "Accord", + "year": 2011 + } + ], + "motorcycles": [ + { + "maker": "Honda", + "model": "ST1300", + "year": 2012 + } + ] +} +``` + +The path expression is **.automobiles{.maker === "Honda" && .year > 2009}.model**. + +``` +jPath(jsonStr, path) +``` + +And it returns the result **['Jazz', 'Accord']**. + + + +### json + +Return the JavaScript Object Notation (JSON) type value or object of a string or XML. + +``` +json('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string or XML | The string or XML to convert | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*JSON-result*> | string | The resulting JSON object created from the specified string or XML. | + +*Example 1* + +This example converts a string to JSON: + +``` +json('{"fullName": "Sophia Owen"}') +``` + +And returns the result: + +``` +{ + "fullName": "Sophia Owen" +} +``` + +*Example 2* + +This example converts XML to JSON: + +``` +json(xml(' Sophia Owen Engineer ')) +``` + +And returns the result: + +``` +{ + "?xml": { "@version": "1.0" }, + "root": { + "person": [ { + "@id": "1", + "name": "Sophia Owen", + "occupation": "Engineer" + } ] + } +} +``` + + + +### jsonStringify + +Return the JSON string of a value. + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | object | The object to convert to a JSON string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*JSON-result*> | string | The resulting JSON string. | + +*Examples* + +These examples show objects converted to JSON strings: + +``` +jsonStringify(null) +jsonStringify({a:'b'}) +``` + +And return the following string results respectively: + +- **null** +- **{\"a\":\"b\"}** + + + +### last + +Return the last item from a collection. + +``` +last('') +last([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection in which to find the last item | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*last-collection-item*> | string or array, respectively | The last item in the collection | + +*Example* + +These examples find the last item in these collections: + +``` +last('abcd') +last(createArray(0, 1, 2, 3)) +``` + +And returns the following results respectively: + +- **d** +- **3** + + + +### lastIndexOf + +Return the starting position or index value of the last occurrence of a substring. This function is case-insensitive, and indexes start with the number 0. + +``` +lastIndexOf('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string or array | The string that has the substring to find | +| <*searchText*> | Yes | string | The substring to find | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*ending-index-value*> | integer | The starting position or index value of the last occurrence of the specified substring. If the string isn't found, return the number **-1**. | + +*Example 1* + +This example finds the starting index value of the last occurrence of the substring **world** in the **hello world** string: + +``` +lastIndexOf('hello world', 'world') +``` + +And returns the result **6**. + +*Example 2* + +This example finds the starting index value of the last occurrence of substring **def** in the array **['abc', 'def', 'ghi', 'def']**. + +``` +lastIndexOf(createArray('abc', 'def', 'ghi', 'def'), 'def') +``` + +And returns the result **3**. + +### length + +Return the length of a string. + +``` +length('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*str*> | Yes | string | The string to calculate for length | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*length*> | integer | The length of this string | + +*Examples* + +These examples get the length of strings: + +``` +length('hello') +length('hello world') +``` + +And returns the following results respectively: + +- **5** +- **11** + + + +### less + +Check whether the first value is less than the second value. Return `true` if the first value is less, or return `false` if the first value is more. + +``` +less(, ) +less('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | integer, float, or string | The first value to check whether less than the second value | +| <*compareTo*> | Yes | integer, float, or string, respectively | The comparison item | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the first value is less than the second value. Return `false` if the first value is equal to or greater than the second value. | + +*Examples* + +These examples check whether the first value is less than the second value. + +``` +less(5, 10) +less('banana', 'apple') +``` + +And return the following results respectively: + +- `true` +- `false` + + + +### lessOrEquals + +Check whether the first value is less than or equal to the second value. Return `true` if the first value is less than or equal, +or return `false` if the first value is more. + +``` +lessOrEquals(, ) +lessOrEquals('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | integer, float, or string | The first value to check whether less than or equal to the second value | +| <*compareTo*> | Yes | integer, float, or string, respectively | The comparison item | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the first value is less than or equal to the second value. Return `false` if the first value is greater than the second value. | + +*Example* + +These examples check whether the first value is less than or equal to the second value. + +``` +lessOrEquals(10, 10) +lessOrEquals('apply', 'apple') +``` + +And return the following results respectively: + +- `true` +- `false` + + + +### max + +Return the highest value from a list or array. The list or array is inclusive at both ends. + +``` +max(, , ...) +max([, , ...]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number1*>, <*number2*>, ... | Yes | number | The set of numbers from which you want the highest value | +| [<*number1*>, <*number2*>, ...] | Yes | array of numbers | The array of numbers from which you want the highest value | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*max-value*> | number | The highest value in the specified array or set of numbers | + +*Examples* + +These examples get the highest value from the set of numbers and the array: + +``` +max(1, 2, 3) +max(createArray(1, 2, 3)) +``` + +And return the result **3**. + + + +### merge + +Merges multiple JSON objects or an array of objects together. + +``` +merge(, , ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*json1*>, <*json2*>, ... | Yes | objects or array | The set of JSON objects or array to merge together. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*result*> | object | The combined JSON object or combined array objects. | + +*Examples* + +Say you have the following JSON objects: + +```json +json1 = @"{ + 'FirstName': 'John', + 'LastName': 'Smith', + 'Enabled': false, + 'Roles': [ 'User' ] + }" +json2 =@"{ + 'Enabled': true, + 'Roles': [ 'User', 'Admin' ] + }" +``` + +This example merges the JSON objects: + +``` +string(merge(json(json1), json(json2))) +``` + +And returns the resulting object **{"FirstName":"John","LastName":"Smith","Enabled":true,"Roles":["User","Admin"]}**. + +Say you want to combine objects and a list of objects together. The following example combines JSON object and an array of objects: + +``` +merge({k1:'v1'}, [{k2:'v2'}, {k3: 'v3'}], {k4:'v4'}) +``` + +And returns the object **{ "k1": "v1", "k2": "v2", "k3": "v3", "k4": "v4" }**. + + + +### min + +Return the lowest value from a set of numbers or an array. + +``` +min(, , ...) +min([, , ...]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number1*>, <*number2*>, ... | Yes | number | The set of numbers from which you want the lowest value | +| [<*number1*>, <*number2*>, ...] | Yes | array of numbers | The array of numbers from which you want the lowest value | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*min-value*> | number | The lowest value in the specified array or set of numbers | + +*Examples* + +These examples get the lowest value in the set of numbers and the array: + +``` +min(1, 2, 3) +min(createArray(1, 2, 3)) +``` + +And return the result **1**. + + + +### mod + +Return the remainder from dividing two numbers. To get the integer result, see [div()](#div). + +``` +mod(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*dividend*> | Yes | number | The number to divide by the *divisor* | +| <*divisor*> | Yes | number | The number that divides the *dividend*. Can't be 0. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*modulo-result*> | number | The remainder from dividing the first number by the second number | + +*Example* + +This example divides the first number by the second number: + +``` +mod(3, 2) +``` + +And returns the result **1**. + + + +### month + +Return the month of the specified timestamp. + +``` +month('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*number-of-month*> | integer | The number of the month in the specified timestamp | + +*Example* + +``` +month('2018-03-15T13:01:00.000Z') +``` + +And it returns the result **3**. + + + +### mul + +Return the product from multiplying two numbers. + +``` +mul(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*multiplicand1*> | Yes | integer or float | The number to multiply by *multiplicand2* | +| <*multiplicand2*> | Yes | integer or float | The number that multiples *multiplicand1* | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*product-result*> | integer or float | The product from multiplying the first number by the second number | + +*Examples* + +These examples multiple the first number by the second number: + +``` +mul(1, 2) +mul(1.5, 2) +``` + +And return the following results respectively: + +- **2** +- **3** + + + +### newGuid + +Return a new Guid string. + +``` +newGuid() +``` + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*Guid-string*> | string | A new Guid string, length is 36 and looks like *xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx*| + +*Example* + +``` +newGuid() +``` + +And it returns a result which follows the format **xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx**. + + + +### not + +Check whether an expression is false. Return `true` if the expression is false, or return `false` if true. + +``` +not() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*expression*> | Yes | Boolean | The expression to check | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the expression is false. Return `false` if the expression is true. | + +*Example 1* + +These examples check whether the specified expressions are false: + +``` +not(false) +not(true) +``` + +And return the following results respectively: + +- The expression is false, so the function returns `true`. +- The expression is true, so the function returns `false`. + +*Example 2* + +These examples check whether the specified expressions are false: + +``` +not(equals(1, 2)) +not(equals(1, 1)) +``` + +And return the following results respectively: + +- The expression is false, so the function returns `true`. +- The expression is true, so the function returns `false`. + + + +### or + +Check whether at least one expression is true. Return `true` if at least one expression is true, +or return `false` if all are false. + +``` +or(, , ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*expression1*>, <*expression2*>, ... | Yes | Boolean | The expressions to check | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if at least one expression is true. Return `false` if all expressions are false. | + +*Example 1* + +These examples check whether at least one expression is true: + +``` +or(true, false) +or(false, false) +``` + +And return the following results respectively: + +- At least one expression is true, so the function returns `true`. +- Both expressions are false, so the function returns `false`. + +*Example 2* + +These examples check whether at least one expression is true: + +``` +or(equals(1, 1), equals(1, 2)) +or(equals(1, 2), equals(1, 3)) +``` + +And return the following results respectively: + +- At least one expression is true, so the function returns `true`. +- Both expressions are false, so the function returns `false`. + + + +### rand + +Return a random integer from a specified range, which is inclusive only at the starting end. + +``` +rand(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*minValue*> | Yes | integer | The lowest integer in the range | +| <*maxValue*> | Yes | integer | The integer that follows the highest integer in the range that the function can return | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*random-result*> | integer | The random integer returned from the specified range | + +*Example* + +This example gets a random integer from the specified range, excluding the maximum value: + +``` +rand(1, 5) +``` + +And returns **1**, **2**, **3**, or **4** as the result. + + + +### range + +Return an integer array that starts from a specified integer. + +``` +range(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*startIndex*> | Yes | integer | An integer value that starts the array as the first item | +| <*count*> | Yes | integer | The number of integers in the array | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*range-result*> | integer | The array with integers starting from the specified index | + +*Example* + +This example creates an integer array that starts from the specified index **1** and has the specified number of integers as **4**: + +``` +range(1, 4) +``` + +And returns the result **[1, 2, 3, 4]**. + + + +### removeProperty + +Remove a property from an object and return the updated object. + +``` +removeProperty(, '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object*> | Yes | object | The JSON object in which you want to remove a property | +| <*property*> | Yes | string | The name of the property to remove | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-object*> | object | The updated JSON object without the specified property | + +*Example* + +This example removes the `accountLocation` property from a `customerProfile` object, which is converted to JSON with the [json()](#json) function, and returns the updated object: + +``` +removeProperty(json('customerProfile'), 'accountLocation') +``` + + + +### replace + +Replace a substring with the specified string, and return the result string. This function is case-sensitive. + +``` +replace('', '', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string that has the substring to replace | +| <*oldText*> | Yes | string | The substring to replace | +| <*newText*> | Yes | string | The replacement string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-text*> | string | The updated string after replacing the substring. If the substring isn't found, the function returns the original string. | + +*Example 1* + +This example finds the substring **old** in **the old string** and replaces **old** with **new**: + +``` +replace('the old string', 'old', 'new') +``` + +The result is the string **the new string**. + +*Example 2* + +When dealing with escape characters, the expression engine handles the unescape for you. This function replaces strings with escape characters. + +``` +replace('hello\"', '\"', '\n') +replace('hello\n', '\n', '\\\\') +@"replace('hello\\', '\\', '\\\\')" +@"replace('hello\n', '\n', '\\\\')" +``` + +And returns the following results respectively: + +- **hello\n** +- **hello\\\\** +- **@"hello\\\\"** +- **@"hello\\\\"** + + + +### replaceIgnoreCase + +Replace a substring with the specified string, and return the result string. This function is case-insensitive. + +``` +replaceIgnoreCase('', '', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string that has the substring to replace | +| <*oldText*> | Yes | string | The substring to replace | +| <*newText*> | Yes | string | The replacement string | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-text*> | string | The updated string after replacing the substring. If the substring isn't found, return the original string. | + +*Example* + +This example finds the substring **old** in the string **the old string** and replaces **old** with **new**: + +``` +replace('the old string', 'old', 'new') +``` + +And returns the result **the new string**. + + + +### resolve + +Return string of a given TimexProperty or Timex expression if it refers to a valid time. Valid time contains hours, minutes, and seconds. + +``` +resolve(' | Yes | string | The string that contains the timestamp. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*return*> | string| A string of the evaluated TimexProperty or Timex expression. | + + +*Examples* + +These examples show if the given strings refer to valid time: + +``` +resolve(T14) +resolve(2020-12-20) +resolve(2020-12-20T14:20) +``` + +And returns the following results respectively: + +- **14:00:00** +- **2020-12-20** +- **2020-12-20 14:20:00** + + + +### reverse + +Reverse the order of the elements in a string or array. + +``` +reverse() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string or array | The string to array to reverse. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*return*> | string or array | The reversed elements of a string or array. | + +*Examples* + +These examples reverse the elements of a string or array: + +``` +reverse(hello) +reverse(concat(hello,world)) +``` + +And return the following values respectively: + +-The string **olleh**. +-The string **dlrowolleh**. + + + + +### round + +Round a value to the nearest integer or to the specified number of fractional digits. + +``` +round('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | An input number | +| <*precision-digits*> | No | integer | A specified number of fractional digits. The default is 0. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*return-value*> | number | The return value of the input rounded at a specified number of fractional digits | + +*Example 1* + +This example rounds the number **10.333**: + +``` +round(10.333) +``` + +And returns the number **10**. + +*Example 2* + +This example rounds the number **10.3313** to **2** fractional digits: + +``` +round(10.3313, 2) +``` + +And returns the number **10.33**. + + + + +### select + +Operate on each element and return the new collection of transformed elements. + +``` +select([], , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection/instance*> | Yes | array | The collection with the items | +| <*iteratorName*> | Yes | iterator name | The key item | +| <*function*> | Yes | expression | Th function that can contains `iteratorName` | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | The new collection in which each element has been evaluated with the function | + +*Example 1* + +This example generates a new collection: + +``` +select(createArray(0, 1, 2, 3), x, x + 1) +``` + +And returns the result **[1, 2, 3, 4]**. + +*Example 2* + +These examples generate a new collection: + +``` +select(json("{'name': 'jack', 'age': '15'}"), x, concat(x.key, ':', x.value)) +select(json("{'name': 'jack', 'age': '15'}"), x=> concat(x.key, ':', x.value)) + +``` + +And return the result **['name:jack', 'age:15']**. Note that the second expression is a *lambda expression*, which some find more readable. + + + +### sentenceCase + +Capitalize the first letter of the first word in a string in an optional locale format. + +``` +sentenceCase('', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The original string | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| result string | string | Returns the sentence case result | + +*Example 1* + +These examples capitalize the first letter in a string: + +``` +sentenceCase('a') +sentenceCase('abc def') +sentenceCase('aBC dEF') +``` + +And return the following results respectively: + +- **A** +- **Abc def** +- **Abc def** + +*Example 2* + +These examples capitalizes the first letter in a string in the specified locale format: + +``` +sentenceCase('a', 'fr-FR') +sentenceCase('abc', 'en-US') +sentenceCase('aBC', 'fr-FR') +``` + +And return the following results respectively: + +- **A** +- **Abc** +- **Abc** + + + +### setPathToValue + +Retrieve the value of the specified property from the JSON object. + +``` +setPathToValue(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*Path*> | Yes | object | The path which you want to set | +| <*value*> | Yes | object | The value you want to set to the path | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| value | object | The value be set | + +*Example 1* + +The example below sets the value **1** to the path: + +``` +setPathToValue(path.x, 1) +``` + +And returns the result **1**. `path.x` is set to **1**. + +*Example 2* + +This example below sets the value: + +``` +setPathToValue(path.array[0], 7) + path.array[0] +``` + +And returns the result **14**. + + + +### setProperty + +Set the value of an object's property and return the updated object. To add a new property, use this function or the [addProperty()](#addProperty) function. + +``` +setProperty(, '', ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object*> | Yes | object | The JSON object in which you want to set a property | +| <*property*> | Yes | string | The name of the property to set | +| <*value*> | Yes | any | The value to set for the specified property | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-object*> | object | The updated JSON object whose property you set | + +*Example* + +This example sets the `accountNumber` property on a `customerProfile` object, which is converted to JSON with the [json()](#json) function. The function assigns a value generated by the [newGuid()](#newGuid) function, and returns the updated JSON object: + +``` +setProperty(json('customerProfile'), 'accountNumber', newGuid()) +``` + + + +### skip + +Remove items from the front of a collection, and return all the other items. + +``` +skip([], ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | array | The collection whose items you want to remove | +| <*count*> | Yes | integer | A positive integer for the number of items to remove at the front | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updatedCollection*> | array | The updated collection after removing the specified items | + +*Example* + +This example removes one item, the number *1*, from the front of the specified array: + +``` +skip(createArray(0, 1, 2, 3), 1) +``` + +And returns an array with the remaining items: **[1,2,3]**. + + + +### sortBy + +Sort elements in the collection in ascending order and return the sorted collection. + +``` +sortBy([], '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection to sort | +| <*property*> | No | string | Sort by this specific property of the object element in the collection if set| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | The new collection whose elements have been sorted | + +*Example 1* + +This example generates sorts the following collection: + +``` +sortBy(createArray(1, 2, 0, 3)) +``` + +And return the result **[0, 1, 2, 3]**. + +*Example 2* + +Suppose you have the following collection: + +```json +{ + 'nestedItems': [ + {'x': 2}, + {'x': 1}, + {'x': 3} + ] +} +``` + +This example generates a new sorted collection based on the **x** object property + +``` +sortBy(nestedItems, 'x') +``` + +And returns the result: + +```json +{ + 'nestedItems': [ + {'x': 1}, + {'x': 2}, + {'x': 3} + ] +} +``` + + + +### sortByDescending + +Sort elements in the collection in descending order, and return the sorted collection. + +``` +sortBy([], '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection to sort | +| <*property*> | No | string | Sort by this specific property of the object element in the collection if set| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | The new collection whose elements have been sorted | + +*Example 1* + +This example generates a new sorted collection: + +``` +sortByDescending(createArray(1, 2, 0, 3)) +``` + +And returns the result **[3, 2, 1, 0]**. + +*Example 2* + +Suppose you have the following collection: + +```json +{ + 'nestedItems': [ + {'x': 2}, + {'x': 1}, + {'x': 3} + ] +} +``` + +This example generates a new sorted collection based on the **x** object property: + +``` +sortByDescending(nestedItems, 'x') +``` + +And returns this result: + +```json +{ + 'nestedItems': [ + {'x': 3}, + {'x': 2}, + {'x': 1} + ] +} +``` + + + +### split + +Return an array that contains substrings, separated by commas, based on the specified delimiter character in the original string. + +``` +split('', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to separate into substrings based on the specified delimiter in the original string. If the text is a null value, it will be taken as an empty string. | +| <*delimiter*> | No | string | The character in the original string to use as the delimiter. If no delimiter provided or the delimiter is a null value, the default value will be an empty string. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| [<*substring1*>,<*substring2*>,...] | array | An array that contains substrings from the original string, separated by commas | + +*Examples* + +These examples create an array with substrings from the specified string based on the specified delimiter character: + +``` +split('a**b**c', '**') +split('hello', '') +split('', 'e') +split('', '') +split('hello') +``` + +And returns the following arrays as the result respectively: + +- **["a", "b", "c"]** +- **["h", "e", "l", "l", "o"]** +- **[""]**, **[ ]** +- **["h", "e", "l", "l", "o"]** + + + +### sqrt + +Return the square root of a specified number. + +``` +sqrt() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*number*> | Yes | number | Number to get square root of of | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result*> | number| The result from computing the square root.| + +*Examples* + +These examples compute the square root of specified numbers: + +``` +sqrt(9) +sqrt(0) +``` + +And return the following results respectively: + +- **3** +- **0** + + + +### startOfDay + +Return the start of the day for a timestamp in an optional locale format. + +``` +startOfDay('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| '<*updated-timestamp*>'| string | The specified timestamp starting at the zero-hour mark for the day | + +*Example 1* + +This example finds the start of the day: + +``` +startOfDay('2018-03-15T13:30:30.000Z') +``` + +And returns the result **2018-03-15T00:00:00.000Z**. + +*Example 2* + +This example finds the start of the day with the locale **fr-FR**: + +``` +startOfDay('2018-03-15T13:30:30.000Z', '', 'fr-FR') +``` + +And returns the result **15/03/2018 00:00:00**. + + + +### startOfHour + +Return the start of the hour for a timestamp in an optional locale format. + +``` +startOfHour('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| '<*updated-timestamp*>'| string | The specified timestamp starting at the zero-minute mark for the day | + +*Example 1* + +This example finds the start of the hour: + +``` +startOfHour('2018-03-15T13:30:30.000Z') +``` + +And returns the result **2018-03-15T13:00:00.000Z**. + +*Example 2* + +This example finds the start of the hour with the locale **fr-FR**: + +``` +startOfHour('2018-03-15T13:30:30.000Z', '', 'fr-FR') +``` + +And returns the result **15/03/2018 13:00:00**. + + + +### startOfMonth + +Return the start of the month for a timestamp in an optional locale format. + +``` +startOfMonth('', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| '<*updated-timestamp*>'| string | The specified timestamp starting on the first day of the month at the zero-hour mark | + +*Example 1* + +This example finds the start of the month: + +``` +startOfMonth('2018-03-15T13:30:30.000Z') +``` + +And returns the result **2018-03-01T00:00:00.000Z**. + +*Example 2* + +This example finds the start of the month with the locale **fr-FR**: + +``` +startOfMonth('2018-03-15T13:30:30.000Z', '', 'fr-FR') +``` + +And returns the result **01/03/2018 00:00:00**. + + + +### startsWith + +Check whether a string starts with a specific substring. Return `true` if the substring is found, or return `false` if not found. This function is case-insensitive. + +``` +startsWith('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to check | +| <*searchText*> | Yes | string | The starting substring to find | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| true or false | Boolean | Return `true` if the starting substring is found. Return `false` if not found | + +*Example 1* + +This example checks whether the string **hello world** starts with the string **hello**: + +``` +startsWith('hello world', 'hello') +``` + +And returns the result `true`. + +*Example 2* + +This example checks whether the string **hello world** starts with the string **greeting**: + +``` +startsWith('hello world', 'greeting') +``` + +And returns the result `false`. + + + +### string + +Return the string version of a value in an optional locale format. + +``` +string(, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | any | The value to convert | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*string-value*> | string | The string version of the specified value | + +*Example 1* + +This example creates the string version of the number **10**: + +``` +string(10) +``` + +And returns the string result **10**. + +*Example 2* + +This example creates a string for the specified JSON object and uses the backslash character,**\\\\**, as an escape character for the double-quotation mark character, **"**. + +``` +string( { "name": "Sophie Owen" } ) +``` + +And returns the result **{ "name": "Sophie Owen" }** + +*Example 3* + +These example creates a string version of the number **10** in a specific locale: + +``` +string(100.1, 'fr-FR') +string(100.1, 'en-US') +``` + +And returns the following strings respectively: + +- **100,1** +- **100.1** + + + +### stringOrValue + +Wrap string interpolation to get the real value. For example, `stringOrValue('${1}')` returns the number 1, while` stringOrValue('${1} item')` returns the string "1 item". + +``` +stringOrValue() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*string*> | Yes | string | The string to get the real value from. | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*result*> | any | The result of getting the real value of the specified string. | + +*Examples* + +These examples get the real value from the string: + +``` +stringOrValue('${one}') +stringOrValue('${one} item') +``` + +And return the following results respectively: + +- The number **1.0**. +- The string **1 item**. + + + +### sub + +Return the result from subtracting the second number from the first number. + +``` +sub(, ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*minuend*> | Yes | number | The number from which to subtract the *subtrahend* | +| <*subtrahend*> | Yes | number | The number to subtract from the *minuend* | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*result*> | number | The result from subtracting the second number from the first number | + +*Example* + +This example subtracts the second number from the first number: + +``` +sub(10.3, .3) +``` + +And returns the result **10**. + + + +### subArray + +Returns a subarray from specified start and end positions. Index values start with the number 0. + +``` +subArray(, , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*array*> | Yes | array | The array to create the subarray from | +| <*startIndex*> | Yes | integer | A positive number equal to or greater than 0 to use as the starting position or index value | +| <*endIndex*> | Yes | integer | A positive number equal to or greater than 0 to use as the ending position or index value| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*subarray-result*> | array | A subarray with the specified number of items, starting at the specified index position in the source string | + +*Example* + +This example creates a subarray from the specified array: + +``` +subArray(createArray('H','e','l','l','o'), 2, 5) +``` + +And returns the result **["l", "l", "o"]**. + + + +### substring + +Return characters from a string, starting from the specified position or index. Index values start with the number 0. + +``` +substring('', , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to create the substring from | +| <*startIndex*> | Yes | integer | A positive number equal to or greater than 0 subarray to use as the starting position or index value | +| <*length*> | Yes | integer | A positive number of characters subarray in the substring | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*substring-result*> | string | A substring with the specified number of characters, starting at the specified index position in the source string | + +*Example* + +This example creates a five-character substring from the specified string, starting from the index value 6: + +``` +substring('hello world', 6, 5) +``` + +And returns the result **world**. + + + +### subtractFromTime + +Subtract a number of time units from a timestamp in an optional locale format. See also [getPastTime()](#getPastTime). + +``` +subtractFromTime('', , '', ''?, ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | +| <*interval*> | Yes | integer | The number of specified time units to subtract | +| <*timeUnit*> | Yes | string | The unit of time to use with *interval*. Possible units are "Second", "Minute", "Hour", "Day", "Week", "Month", and "Year". | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updated-timestamp*> | string | The timestamp minus the specified number of time units | + +*Example 1* + +This example subtracts one day from a following timestamp: + +``` +subtractFromTime('2018-01-02T00:00.000Z', 1, 'Day') +``` + +And returns the result **2018-01-01T00:00:00.000Z**. + +*Example 2* + +This example subtracts one day from a timestamp using the **D** format: + +``` +subtractFromTime('2018-01-02T00:00.000Z', 1, 'Day', 'D') +``` + +And returns the result **Monday, January, 1, 2018**. + +*Example 3* + +This example subtracts **1** hour from a timestamp in the **de-DE** locale: + +``` +subtractFromTime('2018-03-15T13:00:00.000Z', 1, 'Hour', '', 'de-DE') +``` + +And returns the result **15.03.18 12:00:00**. + + + +### sum + +Return the result from adding numbers in a list. + +``` +sum([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| [\] | Yes | array of numbers | The numbers to add | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result-sum*> | number | The result from adding the specified numbers | + +*Example* + +This example adds the specified numbers: + +``` +sum(createArray(1, 1.5)) +``` + +And returns the result **2.5**. + + + +### take + +Return items from the front of a collection. + +``` +take('', ) +take([], ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | string or array | The collection whose items you want | +| <*count*> | Yes | integer | A positive integer for the number of items you want from the front | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*subset*> or [<*subset*>]| String or Array, respectively | A string or array that has the specified number of items taken from the front of the original collection | + +*Example* + +These examples get the specified number of items from the front of these collections: + +``` +take('abcde', 3) +take(createArray(0, 1, 2, 3, 4), 3) +``` + +And return the following results respectively: + +- **abc** +- **[0, 1, 2]** + + + +### ticks + +Return the ticks property value of a specified timestamp. A tick is 100-nanosecond interval. + +``` +ticks('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*>| Yes | string | The string for a timestamp | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*ticks-number*> | integer (bigint in JavaScript)| The number of ticks since the specified timestamp | + +*Example* + +This example converts a timestamp to its ticks property: + +``` +ticks('2018-01-01T08:00:00.000Z') +``` + +And returns the result **636503904000000000**. + + + +### ticksToDays + +Convert a ticks property value to the number of days. + +``` +ticksToDays('ticks') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*ticks*>| Yes | integer | The ticks property value to convert | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*number-of-days*> | number | The number of days converted from the ticks property value | + +*Example* + +This example converts a ticks property value to a number of days: + +``` +ticksToDays(2193385800000000) +``` + +And returns the number **2538.64097222**. + + + +### ticksToHours + +Convert a ticks property value to the number of hours. + +``` +ticksToHours('ticks') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*ticks*>| Yes | Integer | The ticks property value to convert | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*number-of-hours*> | number | The number of hours converted from the ticks property value | + +*Example* + +This example converts a ticks property value to a number of hours: + +``` +ticksToHours(2193385800000000) +``` + +And returns the number **60927.383333333331**. + + + +### ticksToMinutes + +Convert a ticks property value to the number of minutes. + +``` +ticksToMinutes('ticks') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*ticks*>| Yes | integer | The ticks property value to convert | + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*number-of-minutes*> | number | The number of minutes converted from the ticks property value | + +*Example* + +This example converts a ticks property value to a number of minutes: + +``` +ticksToMinutes(2193385800000000) +``` + +And returns the number **3655643.0185**. + + + +### titleCase + +Capitalize the first letter of each word in a string in an optional local format. + +``` +titleCase('', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The original string | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| result string | string | The title case result | + +*Example 1* + +These examples capitalize the first letter of each word in a string: + +``` +titleCase('a') +titleCase('abc def') +titleCase('aBC dEF') +``` + +And return the following results respectively: + +- **A** +- **Abc Def** +- **Abc Def** + +*Example 2* + +These examples capitalize the first letter in a string in the **en-US** format: + +``` +titleCase('a', 'en-US') +titleCase('aBC dEF', 'en-US') +``` + +And return the following results respectively: + +- **A** +- **Abc Def** + + + +### toLower + +Return a string in lowercase in an optional locale format. If a character in the string doesn't have a lowercase version, that character stays unchanged in the returned string. + +``` +toLower('', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to return in lowercase format | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*lowercase-text*> | string | The original string in lowercase format | + +*Example 1* + +This example converts a string to lowercase: + +``` +toLower('Hello World') +``` + +And returns the result **hello world**. + +*Example 2* + +This example converts a string to lowercase in the **fr-FR** format: + +``` +toUpper('Hello World', 'fr-FR') +``` + +And returns the result **hello world**. + + + +### toUpper + +Return a string in uppercase in an optional locale format. If a character in the string doesn't have an uppercase version, that character stays unchanged in the returned string. + +``` +toUpper('', ''?) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string to return in uppercase format | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*uppercase-text*> | string | The original string in uppercase format | + +*Example 1* + +This example converts a string to uppercase: + +``` +toUpper('Hello World') +``` + +And returns the result **HELLO WORLD**. + +*Example 2* + +This example converts a string to uppercase in the **fr-FR** format: + +``` +toUpper('Hello World', 'fr-FR') +``` + +And returns the result **HELLO WORLD**. + + + +### trim + +Remove leading and trailing whitespace from a string, and return the updated string. + +``` +trim('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*text*> | Yes | string | The string that has the leading and trailing whitespace to remove | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updatedText*> | string | An updated version for the original string without leading or trailing whitespace | + +*Example* + +This example removes the leading and trailing whitespace from the string **" Hello World "**: + +``` +trim(' Hello World ') +``` + +And returns the trimmed result **Hello World**. + + + +### union + +Return a collection that has all the items from the specified collections. To appear in the result, an item can appear in any collection passed to this function. If one or more items have the same name, the last item with that name appears in the result. + +``` +union('', '', ...) +union([], [], ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection1*>, <*collection2*>, ...| Yes | array or object, but not both | The collections from where you want all the items | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*updatedCollection*> | array or object, respectively | A collection with all the items from the specified collections. No duplicates added. | + +*Example* + +This example gets all the items from the following collections: + +``` +union(createArray(1, 2, 3), createArray(1, 2, 10, 101)) +``` + +And returns the result **[1, 2, 3, 10, 101]**. + + + +### unique + +Remove all duplicates from an array. + +``` +unique([]) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection*> | Yes | array | The collection to modify | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection*> | array | New collection with duplicate elements removed | + +*Example 1* + +This example removes duplicate elements from the following array: + +``` +unique(createArray(1, 2, 1)) +``` + +And returns the result **[1, 2]**. + + + +### uriComponent + +Return the binary version of a uniform resource identifier (URI) component. + +``` +uriComponent('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The string to convert to URI-encoded format | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*encoded-uri*> | string | The URI-encoded string with escape characters | + +*Example* + +This example creates a URI-encoded version of a string: + +``` +uriComponent('https://contoso.com') +``` + +And returns the result **http%3A%2F%2Fcontoso.com**. + + + +### uriComponentToString + +Return the string version of a uniform resource identifier (URI) encoded string, effectively decoding the URI-encoded string. + +``` +uriComponentToString('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The URI-encoded string to decode | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*binary-for-encoded-uri*> | string | The decoded version for the URI-encoded string | + +*Example* + +This example creates the decoded string version of a URI-encoded string: + +``` +uriComponentToString('http%3A%2F%2Fcontoso.com') +``` + +And returns the result `https://contoso.com`. + + + +### uriHost + +Return the host value of a unified resource identifier (URI). + +``` +uriHost('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose host value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*host-value*> | string | The host value of the specified URI | + +*Example* + +This example finds the host value of the following URI: + +``` +uriHost('https://www.localhost.com:8080') +``` + +And returns the result `www.localhost.com`. + + + +### uriPath + +Return the path value of a unified resource identifier (URI). + +``` +uriPath('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose path value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*path-value*> | string | The path value of the specified URI | + +*Example* + +This example finds the path value of the following URI: + +``` +uriPath('http://www.contoso.com/catalog/shownew.htm?date=today') +``` + +And returns the result **/catalog/shownew.htm**. + + + +### uriPathAndQuery + +Return the path and query value of a unified resource identifier (URI). + +``` +uriPathAndQuery('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose path and query value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*path-query-value*> | string | The path and query value of the specified URI | + +*Example* + +This example finds the path and query value of the following URI: + +``` +uriPathAndQuery('http://www.contoso.com/catalog/shownew.htm?date=today') +``` + +And returns the result **/catalog/shownew.htm?date=today**. + + + +### uriPort + +Return the port value of a unified resource identifier (URI). + +``` +uriPort('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose path value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*port-value*> | string | The port value of the specified URI | + +*Example* + +This example finds the port value of the following URI: + +``` +uriPort('http://www.localhost:8080') +``` + +And returns the result **8080**. + + + +### uriQuery + +Return the query value of a unified resource identifier (URI). + +``` +uriQuery('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose query value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*query-value*> | string | The query value of the specified URI | + +*Example* + +This example finds the query value of the following URI: + +``` +uriQuery('http://www.contoso.com/catalog/shownew.htm?date=today') +``` + +And returns the result **?date=today**. + + + +### uriScheme + +Return the scheme value of a unified resource identifier (URI). + +``` +uriScheme('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*uri*> | Yes | string | The URI whose query value you want | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*scheme-value*> | string | The scheme value of the specified URI | + +*Example* + +This example finds the scheme value of the following URI: + +``` +uriQuery('http://www.contoso.com/catalog/shownew.htm?date=today') +``` + +And returns the result **http**. + + + +### utcNow + +Return the current timestamp in an optional locale format as a string. + +``` +utcNow('', ''?) +``` + +Optionally, you can specify a different format with the <*format*> parameter. + + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*format*> | No | string | A [custom format pattern](/dotnet/standard/base-types/custom-date-and-time-format-strings). The default format for the timestamp is UTC ISO format, YYYY-MM-DDTHH:mm:ss.fffZ, which complies with [ISO 8601](https://www.w3.org/QA/Tips/iso-date). | +| <*locale*> | No | string | An optional locale of culture information | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*current-timestamp*> | string | The current date and time | + +*Example 1* + +Suppose the date is **April 15, 2018** at **1:00:00 PM**. This example gets the timestamp: + +``` +utcNow() +``` + +And returns the result **2018-04-15T13:00:00.000Z**. + +*Example 2* + +Suppose the date is **April 15, 2018** at **1:00:00 PM**. This example gets the current timestamp using the optional **D** format: + +``` +utcNow('D') +``` + +And returns the result **Sunday, April 15, 2018**. + +*Example 3* + +Suppose the date is **April 15, 2018** at **1:00:00 PM**. This example gets the current timestamp using the **de-DE** locale: + +``` +utcNow('', 'de-DE') +``` + +And returns the result **15.04.18 13:00:00**. + + + +### where + +Filter on each element and return the new collection of filtered elements which match a specific condition. + +``` +where([], , ) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*collection/instance*> | Yes | array | The collection with the items | +| <*iteratorName*> | Yes | iterator name | The key item | +| <*function*> | Yes | expression | Condition function used to filter items| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*new-collection/new-object*> | array/object | The new collection which each element has been filtered with the function | + +*Example 1* + +This example generates a new collection: + +``` +where(createArray(0, 1, 2, 3), x, x > 1) +``` + +And returns the result **[2, 3]**. + +*Example 2* + +These examples generate a new collection: + +``` +where(json("{'name': 'jack', 'age': '15'}"), x, x.value == 'jack') +where(json("{'name': 'jack', 'age': '15'}"), x=> x.value == 'jack') +``` + +And return the result **['name:jack', 'age:15']**. Note that the second expression is a *lambda expression*, which some find more readable. + + + +### xml + +Return the XML version of a string that contains a JSON object. + +``` +xml('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*value*> | Yes | string | The string with the JSON object to convert. The JSON object must have only one root property, which can't be an array. Use **\\** as an escape character for the double quotation mark (").| + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*xml-version*> | object | The encoded XML for the specified string or JSON object | + +*Example 1* + +This example creates the XML version for a string, which contains a JSON object: + +`xml(json('{ \"name\": \"Sophia Owen\" }'))` + +And returns the result XML: + +```xml +Sophia Owen +``` + +*Example 2* + +Suppose you have a `person` JSON object, seen below: + +```json +{ + "person": { + "name": "Sophia Owen", + "city": "Seattle" + } +} +``` + +This example creates XML of a string that contains this JSON object: + +`xml(json('{\"person\": {\"name\": \"Sophia Owen\", \"city\": \"Seattle\"}}'))` + +And returns the result XML: + +```xml + + Sophia Owen + Seattle + + +### xPath + +Check XML for nodes or values that match an XPath (XML Path Language) expression, and return the matching nodes or values. An XPath expression (referred to as XPath) helps you navigate an XML document structure so that you can select nodes or compute values in the XML content. + +``` +xPath('', '') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*xml*> | Yes | any | The XML string to search for nodes or values that match an XPath expression value | +| <*xPath*> | Yes | any | The XPath expression used to find matching XML nodes or values | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*xml-node*> | XML | An XML node when only a single node matches the specified XPath expression | +| <*value*> | any | The value from an XML node when only a single value matches the specified XPath expression | +| <*[\, \, ...] -or- [\, \, ...]*> | array | An array with XML nodes or values that match the specified XPath expression | + +*Example 1* + +This example finds nodes that match the `` node in the specified arguments, and returns an array with those node values: + +``` +xPath(items, '/produce/item/name') +``` + +The arguments include the **items** string, which contains this XML: + +```xml +" Gala apple 20 Honeycrisp apple 10 " +``` + +Here's the resulting array with the nodes that match ``: + +```json +[ Gala, Honeycrisp ] +``` + +*Example 2* + +Following example 1, this example finds nodes that match the `` node and adds those node values with the [sum()](#sum) function: + +``` +xPath(xml(parameters('items')), 'sum(/produce/item/count)') +``` + +And returns the result **30**. + + + +### year + +Return the year of the specified timestamp. + +``` +year('') +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*timestamp*> | Yes | string | The string that contains the timestamp | + +| Return value | Type | Description | +| ------------ | ---- | ----------- | +| <*year*> | integer | The year in the specified timestamp | + +*Example* + +This example evaluates the timestamp for the year: + +``` +year('2018-03-15T00:00:00.000Z') +``` + +And it returns the result **2018**. diff --git a/articles/bot-builder-deploy-az-cli.md b/articles/bot-builder-deploy-az-cli.md deleted file mode 100644 index f26d75279..000000000 --- a/articles/bot-builder-deploy-az-cli.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Deploy your bot - Bot Service -description: Deploy your bot to the Azure cloud -keywords: deploy bot, azure deploy bot, publish bot -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: conceptual -ms.service: bot-service -ms.date: 08/06/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Deploy your bot - -[!INCLUDE [applies-to](./includes/applies-to.md)] - -In this article we will show you how to deploy a basic bot to Azure. We will explain how to prepare your bot for deployment, deploy your bot to Azure, and test your bot in Web Chat. It would be useful to read this article before following the steps, so that you fully understand what is involved in deploying a bot. - -## Prerequisites - -[!INCLUDE [deploy prerequisite](~/includes/deploy/snippet-prerequisite.md)] - -## Prepare for deployment - -[!INCLUDE [deploy prepare intro](~/includes/deploy/snippet-prepare-deploy-intro.md)] - -### 1. Login to Azure - -[!INCLUDE [deploy az login](~/includes/deploy/snippet-az-login.md)] - -### 2. Set the subscription - -[!INCLUDE [deploy az subscription](~/includes/deploy/snippet-az-set-subscription.md)] - - -### 3. Create the application registration - -[!INCLUDE [deploy create app registration](~/includes/deploy/snippet-create-app-registration.md)] - - -### 4. Create the bot application service - -When creating the bot application service, you can deploy your bot in a new or in an existing resource group. Choose the option that works best for you. - -Make sure that you have the correct path to your bot project ARM deployment templates directory `DeploymentTemplates`, you need it to assign the value to `template-file`. - - -> [!NOTE] -> Python bots cannot be deployed to a resource group that contains Windows services/bots. Multiple Python bots can be deployed to the same resource group, but create other services (LUIS, QnA, etc.) in another resource group. - - -#### **Deploy via ARM template (with **new** Resource Group)** - - -[!INCLUDE [ARM with new resourece group](~/includes/deploy/snippet-ARM-new-resource-group.md)] - - -#### **Deploy via ARM template (with **existing** Resource Group)** - -[!INCLUDE [ARM with existing resourece group](~/includes/deploy/snippet-ARM-existing-resource-group.md)] - ---- - -### 5. Prepare your code for deployment - -#### 5.1 Retrieve or create necessary IIS/Kudu files - -[!INCLUDE [retrieve or create IIS/Kudu files](~/includes/deploy/snippet-IIS-Kudu-files.md)] - - -#### 5.2 Zip up the code directory manually - -[!INCLUDE [zip up code](~/includes/deploy/snippet-zip-code.md)] - - -## Deploy code to Azure - -[!INCLUDE [deploy code to Azure](~/includes/deploy/snippet-deploy-code-to-az.md)] - - -## Test in Web Chat - -[!INCLUDE [test in web chat](~/includes/deploy/snippet-test-in-web-chat.md)] - - -## Additional information - -Deploying your bot to Azure will involve paying for the services you use. The [billing and cost management](https://docs.microsoft.com/azure/billing/) article helps you understand Azure billing, monitor usage and costs, and manage your account and subscriptions. - -## Next steps - -> [!div class="nextstepaction"] -> [Set up continuous deployment](bot-service-build-continuous-deployment.md) - - diff --git a/articles/bot-builder-howto-answer-questions.md b/articles/bot-builder-howto-answer-questions.md new file mode 100644 index 000000000..32e5bdbeb --- /dev/null +++ b/articles/bot-builder-howto-answer-questions.md @@ -0,0 +1,113 @@ +--- +title: Use question answering to answer questions +description: Learn how bots can answer questions from users without parsing or interpreting the questions. See how to use question answering for this task. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Use question answering to answer questions + +[!INCLUDE [applies-to-v4](./includes/applies-to-v4-current.md)] + +The [question answering](v4sdk/bot-builder-concept-luis.md#question-answering) feature of Azure Cognitive Service for Language provides cloud-based natural language processing (NLP) that allows you to create a natural conversational layer over your data. It's used to find the most appropriate answer for any input from your custom knowledge base of information. + +This article describes how to use the question answering feature in your bot. + +## Prerequisites + +- If you don't have an Azure subscription, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A language resource in [Language Studio](https://language.cognitive.azure.com/), with the custom question answering feature enabled. +- A copy of the **Custom Question Answering** sample in [**C#**][cs sample] or [**JavaScript**][js sample]. + +[cs sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/12.customQABot +[js sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/12.customQABot + +## About this sample + +To use question answering in your bot, you need an existing knowledge base. Your bot then can use the knowledge base to answer the user's questions. + +If you need to create a new knowledge base for a Bot Framework SDK bot, see the README for the custom question answering sample. + +## [C#](#tab/cs) + +:::image type="content" source="./media/bot-builder-howto-answer-questions/flow-diagram-cs.png" alt-text="C# question answering bot logic flow."::: + +`OnMessageActivityAsync` is called for each user input received. When called, it accesses configuration settings from the sample code's **appsetting.json** file and connects to your knowledge base. + +## [JavaScript](#tab/js) + +:::image type="content" source="./media/bot-builder-howto-answer-questions/flow-diagram-js.png" alt-text="JavaScript question answering bot logic flow."::: + +`OnMessage` is called for each user input received. When called, it accesses configuration settings from your sample code's **.env** file and connects to your knowledge base. + +--- + +The user's input is sent to your knowledge base and the best returned answer is displayed back to your user. + +## Get your knowledge base connection settings + +1. In [Language Studio](https://language.cognitive.azure.com/), open your language resource. +1. Copy the following information to your bot's configuration file: + + - The host name for your language endpoint. + - The `Ocp-Apim-Subscription-Key`, which is your endpoint key. + - The project name, which acts as your knowledge base ID. + +Your host name is the part of the endpoint URL between `https://` and `/language`, for example, `https:///language`. Your bot needs the project name, host URL, and endpoint key to connect to your knowledge base. + +> [!TIP] +> If you aren't deploying this for production, you can leave your bot's app ID and password fields blank. + +## Set up and call the knowledge base client + +Create your knowledge base client, then use the client to retrieve answers from the knowledge base. + +## [C#](#tab/cs) + +Be sure that the **Microsoft.Bot.Builder.AI.QnA** NuGet package is installed for your project. + +In **QnABot.cs**, in the `OnMessageActivityAsync` method, create a knowledge base client. Use the turn context to query the knowledge base. + +**Bots/CustomQABot.cs** + +:::code language="csharp" source="~/../botbuilder-samples/samples/csharp_dotnetcore/12.customQABot/Bots/CustomQABot.cs" range="65-72"::: + +## [JavaScript](#tab/js) + +Be sure that npm package **botbuilder-ai** is installed for your project. + +In **CustomQABot.js**, in the constructor, create a knowledge base client. In the **onMessage** method, use the turn context to query the knowledge base. + +**bots/CustomQABot.js** + +:::code language="javascript" source="~/../botbuilder-samples/samples/javascript_nodejs/12.customQABot/bots/CustomQABot.js" range="12-16"::: + +:::code language="javascript" source="~/../botbuilder-samples/samples/javascript_nodejs/12.customQABot/bots/CustomQABot.js" range="46-48"::: + +--- + +## Test the bot + +Run the sample locally on your machine. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md#download). For further instructions, refer to the sample's `README` ([C#][CS readme] or [JavaScript][JS readme]). + +Start the Emulator, connect to your bot, and send messages to your bot. The responses to your questions will vary, based on the information your knowledge base. + +[CS readme]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/12.customQABot#readme +[JS readme]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/12.customQABot#readme + +## Additional information + +The **Custom Question Answering, all features** sample ([**C#**][CS adv readme] or [**JavaScript**][JS adv readme]) shows how to use a _QnA Maker dialog_ to support a knowledge base's follow-up prompt and active learning features. + +- Question answering supports follow-up prompts, also known as multi-turn prompts. If the knowledge base requires more information from the user, the service sends context information that you can use to prompt the user. This information is also used to make any follow-up calls to the service. +- Question answering also supports active learning suggestions, allowing the knowledge base to improve over time. The QnA Maker dialog supports explicit feedback for the active learning feature. + +[CS adv readme]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/48.customQABot-all-features#readme +[JS adv readme]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/48.customQABot-all-features#readme \ No newline at end of file diff --git a/articles/bot-builder-tutorial-authentication.md b/articles/bot-builder-tutorial-authentication.md deleted file mode 100644 index 9715a79f5..000000000 --- a/articles/bot-builder-tutorial-authentication.md +++ /dev/null @@ -1,361 +0,0 @@ ---- -title: Add authentication to your bot via Azure Bot Service - Bot Service -description: Learn how to use the Azure Bot Service authentication features to add SSO to your bot. -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ROBOTS: NOINDEX -ms.date: 11/14/2019 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add authentication to your bot via Azure Bot Service - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -This tutorial uses new bot authentication capabilities in Azure Bot Service, providing features to make it easier to develop a bot that authenticates users to various identity providers such as Azure AD (Azure Active Directory), GitHub, Uber, and so on. These updates also take steps towards an improved user experience by eliminating the _magic code verification_ for some clients. - -Prior to this, your bot needed to include OAuth controllers and login links, store the target client IDs and secrets, and perform user token management. - - -Now, bot developers no longer need to host OAuth controllers or manage the token life-cycle, as all of this can now be done by the Azure Bot Service. - -The features include: - -- Improvements to the channels to support new authentication features, such as new WebChat and DirectLineJS libraries to eliminate the need for the 6-digit magic code verification. -- Improvements to the Azure Portal to add, delete, and configure connection settings to various OAuth identity providers. -- Support for a variety of out-of-the-box identity providers including Azure AD (both v1 and v2 endpoints), GitHub, and others. -- Updates to the C# and Node.js Bot Framework SDKs to be able to retrieve tokens, create OAuthCards and handle TokenResponse events. -- Samples for how to make a bot that authenticates to Azure AD (v1 and v2 endpoints) and to GitHub. - -You can extrapolate from the steps in this article to add such features to an existing bot. The following are sample bots that demonstrate the new authentication features - -| Sample | BotBuilder version | Description | -|:---|:---:|:---| -| [AadV1Bot](https://aka.ms/AadV1Bot) | v3 | Demonstrates OAuthCard support in the v3 C# SDK, using the Azure AD v1 endpoint | -| [AadV2Bot](https://aka.ms/AadV2Bot) | v3 | Demonstrates OAuthCard support in the v3 C# SDK, using the Azure AD v2 endpoint | -| [GitHubBot](https://aka.ms/GitHubBot) | v3 | Demonstrates OAuthCard support in the v3 C# SDK, using GitHub | -| [BasicOAuth](https://aka.ms/BasicOAuth) | v3 | Demonstrates OAuth 2.0 support in the v3 C# SDK | - -> [!NOTE] -> The authentication features also work with Node.js with BotBuilder v3. However, this article covers just sample C# code. - -For additional information and support, refer to [Bot Framework additional resources](https://docs.microsoft.com/azure/bot-service/bot-service-resources-links-help). - -## Overview - -This tutorial creates a sample bot that connects to the Microsoft Graph using an Azure AD v1 or v2 token. As part of this process, you'll use code from a GitHub repo, and this tutorial describes how to set that up, including the bot application. - -- [Create your bot and an authentication application](#create-your-bot-and-an-authentication-application) -- [Prepare the bot sample code](#prepare-the-bot-sample-code) -- [Use the Emulator to test your bot](#use-the-emulator-to-test-your-bot) - -To complete these steps, you will need Visual Studio 2017, npm, node, and git installed. You should also have some familiarity with Azure, OAuth 2.0, and bot development. - -Once you finish, you will have a bot that can respond to a few simple tasks against an Azure AD application, such as checking and sending an email, or displaying who you are and who your manager is. To do this, your bot will use a token from an Azure AD application against the Microsoft.Graph library. - -The final section breaks down some of the bot code - -- [Notes on the token retrieval flow](#notes-on-the-token-retrieval-flow) - -## Create your bot and an authentication application - -You need to create a registration bot where you'll set the messaging endpoint to your deployed bot's code, and you need to create an Azure AD (either v1 or v2) application to allow your bot to access Office 365. - -> [!NOTE] -> These authentication features work with other types of bots. However this tutorial uses a registration only bot. - -### Register an application in Azure AD - -You need an Azure AD application that your bot can use as an identity provider to connect to the Microsoft Graph API. - -For this bot you can use Azure AD v1 or v2 endpoints. -For information about the differences between the v1 and v2 endpoints, see the [v1-v2 comparison](https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-compare) and the [Azure AD v2.0 endpoint overview](https://docs.microsoft.com/azure/active-directory/develop/active-directory-appmodel-v2-overview). - -#### Create an Azure AD identity provider application - -Use these steps to create a new Azure AD application. You can use the v1 or v2 endpoints with the app that you create. - -> [!TIP] -> You will need to create and register the Azure AD application in a tenant -> in which you can consent to delegate permissions requested by an application. - -1. Open the [Azure Active Directory][azure-aad-blade] panel in the Azure portal. - If you are not in the correct tenant, click **Switch directory** to switch to the correct tenant. (For instruction on creating a tenant, see [Access the portal and create a tenant](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-access-create-new-tenant).) -1. Open the **App registrations** panel. -1. In the **App registrations** panel, click **New registration**. -1. Fill in the required fields and create the app registration. - - 1. Name your application. - 1. Select the **Supported account types** for your application. - 1. For the **Redirect URI** - 1. Select **Web**. - 1. Set the URL to `https://token.botframework.com/.auth/web/redirect`. - 1. Click **Register**. - - - Once it is created, Azure displays the **Overview** page for the app. - - Record the **Application (client) ID** value. You will use this value later as the _Client id_ when you register your Azure AD application with your bot. - - Also record the **Directory (tenant) ID** value. You will also use this to register this application with your bot. - - > [!NOTE] - > When the supported account types is set to single tenant, if you use a personal subscription instead of a Microsoft account, the emulator would issue the error: *The bot's Microsoft App ID or Microsoft App Password is incorrect..* - > In this case, the supported account types must be set to *Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)*. - -1. In the navigation pane, click **Certificates & secrets** to create a secret for your application. - - 1. Under **Client secrets**, click **New client secret**. - 1. Add a description to identify this secret from others you might need to create for this app, such as `bot login`. - 1. Set **Expires** to **Never**. - 1. Click **Add**. - 1. Before leaving this page, record the secret. You will use this value later as the _Client secret_ when you register your Azure AD application with your bot. - -1. In the navigation pane, click **API permissions** to open the **API permissions** panel. It is a best practice to explicitly set the API permissions for the app. - - 1. Click **Add a permission** to show the **Request API permissions** pane. - 1. For this sample, select **Microsoft APIs** and **Microsoft Graph**. - 1. Choose **Delegated permissions** and make sure the permissions you need are selected. This sample requires theses permissions. - - > [!NOTE] - > Any permission marked as **ADMIN CONSENT REQUIRED** will require both a user and a tenant admin to login, so for your bot tend to stay away from these. - - - **openid** - - **profile** - - **Mail.Read** - - **Mail.Send** - - **User.Read** - - **User.ReadBasic.All** - - 1. Click **Add permissions**. (The first time a user accesses this app through the bot, they will need to grant consent.) - -You now have an Azure AD application configured. - -### Create your bot on Azure - -Create a **Bot Channels Registration** using the [Azure Portal](https://portal.azure.com/). - -### Register your Azure AD application with your bot - -The next step is to register with your bot the Azure AD application that you just created. - -# [Azure AD v1](#tab/aadv1) - -1. Navigate to your bot's resource page on the [Azure Portal](https://portal.azure.com/). -1. Click **Settings**. -1. Under **OAuth Connection Settings** near the bottom of the page, click **Add Setting**. -1. Fill in the form as follows: - - 1. For **Name**, enter a name for your connection. You'll use this name in your bot code. - 1. For **Service Provider**, select **Azure Active Directory**. Once you select this, the Azure AD-specific fields will be displayed. - 1. For **Client id**, enter the application (client) ID that you recorded for your Azure AD v1 application. - 1. For **Client secret**, enter the secret that you created to grant the bot access to the Azure AD app. - 1. For **Grant Type**, enter `authorization_code`. - 1. For **Login URL**, enter `https://login.microsoftonline.com`. - 1. For **Tenant ID**, enter the directory (tenant) ID that your recorded earlier for your Azure AD app. - - This will be the tenant associated with the users who can be authenticated. - - 1. For **Resource URL**, enter `https://graph.microsoft.com/`. - 1. Leave **Scopes** blank. - -1. Click **Save**. - -> [!NOTE] -> These values enable your application to access Office 365 data via the Microsoft Graph API. - -# [Azure AD v2](#tab/aadv2) - -1. Navigate to your bot's Bot Channels Registration page on the [Azure Portal](https://portal.azure.com/). -1. Click **Settings**. -1. Under **OAuth Connection Settings** near the bottom of the page, click **Add Setting**. -1. Fill in the form as follows: - - 1. For **Name**, enter a name for your connection. You'll use it in your bot code. - 1. For **Service Provider**, select **Azure Active Directory v2**. Once you select this, the Azure AD-specific fields will be displayed. - 1. For **Client id**, enter the application (client) ID that you recorded for your Azure AD v1 application. - 1. For **Client secret**, enter the secret that you created to grant the bot access to the Azure AD app. - 1. For **Tenant ID**, enter the directory (tenant) ID that your recorded earlier for your Azure AD app. - - This will be the tenant associated with the users who can be authenticated. - - 1. For **Scopes**, enter the names of the permission you chose from application registration: - `Mail.Read Mail.Send openid profile User.Read User.ReadBasic.All`. - - > [!NOTE] - > For Azure AD v2, **Scopes** takes a case-sensitive, space-separated list of values. - -1. Click **Save**. - -> [!NOTE] -> These values enable your application to access Office 365 data via the Microsoft Graph API. - ---- - -#### To test your connection - -1. Open the connection you just created. -1. Click **Test Connection** at the top of the **Service Provider Connection Setting** pane. -1. The first time, this should open a new browser tab listing the permissions your app is requesting and prompt you to accept. -1. Click **Accept**. -1. This should then redirect you to a **Test Connection to `' Succeeded** page. - -## Prepare the bot sample code - -1. Clone the github repository at https://github.com/Microsoft/BotBuilder. -1. Open and build the solution, `BotBuilder\CSharp\Microsoft.Bot.Builder.sln`. -1. Close that solution and open, `BotBuilder\CSharp\Samples\Microsoft.Bot.Builder.Samples.sln`. -1. Set the start up project. - - For a bot that uses the v1 Azure AD application, use the `Microsoft.Bot.Sample.AadV1Bot` project. - - For a bot that uses the v2 Azure AD application, use the `Microsoft.Bot.Sample.AadV2Bot` project. -1. Open the `Web.config` file, and modify the app settings as follows: - 1. Set the `ConnectionName` to the value you used when you configured your bot's OAuth 2.0 connection setting. - 1. Set the `MicrosoftAppId` value to your bot's app ID. - 1. Set the `MicosoftAppPassword` value to your bot's secret. - - > [!IMPORTANT] - > Depending on the characters in your secret, you may need to XML escape the password. For example, any ampersands (&) will need to be encoded as `&`. - - ```xml - - - - - - ``` - - If you do not know how to get your **Microsoft app ID** and **Microsoft app password** values, you can either create a new password as described here: - [bot-channels-registration-password](bot-service-quickstart-registration.md#get-registration-password) - Or retrieve the **Microsoft app ID** and **Microsoft app password** provisioned with the **Bot Channels Registration** from the deployement described here: - [find-your-azure-bots-appid-and-appsecret](https://blog.botframework.com/2018/07/03/find-your-azure-bots-appid-and-appsecret) - - > [!NOTE] - > You could now publish this bot code to your Azure subscription (right-click on the project and choose **Publish**), but it is not necessary for this tutorial. You would need to set up a publishing configuration that uses the application and hosting plan that you used when configuration the bot in the Azure Portal. - -## Use the Emulator to test your bot - -You will need to install the [Bot Emulator](https://github.com/Microsoft/BotFramework-Emulator) to test your bot locally. You can use the v3 or v4 Emulator. - -1. Start your bot (with or without debugging). -1. Note the localhost port number for the page. You will need this information to interact with your bot. -1. Start the Emulator. -1. Connect to your bot. - - If you haven't configured the connection already, provide the address and your bot's Microsoft app ID and password. Add `/api/messages` to the bot's URL. Your URL will look something like `http://localhost:portNumber/api/messages`. - -1. Type `help` to see a list of available commands for the bot, and test the authentication features. -1. Once you've signed in, you don't need to provide your credentials again until you sign out. -1. To sign out, and cancel your authentication, type `signout`. - - - -> [!NOTE] -> Bot authentication requires use of the Bot Connector Service. The service accesses the bot channels registration information for your bot, which is why you need to set your bot's messaging endpoint on the portal. Authentication also requires the use of HTTPS, which is why you needed to create an HTTPS forwarding address for your bot running locally. - - - -## Notes on the token retrieval flow - -When a user asks the bot to do something that requires the bot to have the user logged in, the bot can use the `Microsoft.Bot.Builder.Dialogs.GetTokenDialog` to initiate retrieving a token for a given connection. The next couple of snippets are taken from the `GetTokenDialog` class. - -### Check for a cached token - -In this code, first the bot does a quick check to determine if the Azure Bot Service already has a token for the user (which is identified by the current Activity sender) and the given ConnectionName (which is the connection name used in configuration). Azure Bot Service will either already have a token cached or it will not. The call to GetUserTokenAsync performs this ‘quick check'. If Azure Bot Service has a token and returns it, the token can immediately be used. If Azure Bot Service does not have a token, this method will return null. In this case, the bot can send a customized OAuthCard for the user to login. - -```csharp -// First ask Bot Service if it already has a token for this user -var token = await context.GetUserTokenAsync(ConnectionName).ConfigureAwait(false); -if (token != null) -{ - // use the token to do exciting things! -} -else -{ - // If Bot Service does not have a token, send an OAuth card to sign in - await SendOAuthCardAsync(context, (Activity)context.Activity); -} -``` - -### Send an OAuthCard to the user - -You can customize the OAuthCard with whatever text and button text you want. The important pieces are: - -- Set the `ContentType` to `OAuthCard.ContentType`. -- Set the `ConnectionName` property to the name of the connection you want to use. -- Include one button with a `CardAction` of `Type` `ActionTypes.Signin`; note that you do not need to specify any value for the sign in link. - -At the end of this call, the bot needs to "wait for the token" to come back. This waiting takes place on the main Activity stream because there could be a lot the user needs to do to sign-in. - -```csharp -private async Task SendOAuthCardAsync(IDialogContext context, Activity activity) -{ - await context.PostAsync($"To do this, you'll first need to sign in."); - - var reply = await context.Activity.CreateOAuthReplyAsync(_connectionName, _signInMessage, _buttonLabel).ConfigureAwait(false); - await context.PostAsync(reply); - - context.Wait(WaitForToken); -} -``` - -### Wait for a TokenResponseEvent - -In this code the Bot's dialog class is waiting for a `TokenResponseEvent` (more about how this is routed to the Dialog stack is below). The `WaitForToken` method first determines if this event was sent. If it was sent, it can be used by the bot. If it was not, the `WaitForToken` method takes whatever text was sent to the bot and passes it to `GetUserTokenAsync`. The reason for this is that some clients (like WebChat) do not need the Magic Code verification code and can directly send the Token in the `TokenResponseEvent`. Other clients still require the magic code (like Facebook or Slack). The Azure Bot Service will present these clients with a six digit magic code and ask the user to type this into the chat window. While not ideal, this is the 'fall back' behavior and so if `WaitForToken` receives a code, the bot can send this code to the Azure Bot Service and get a token back. If this call also fails, then you can decide to report an error, or do something else. In most cases though, the bot will now have a user token. - -If you look in the **MessageController.cs** file, you'll see that `Event` activities of this type are also routed to the dialog stack. - -```csharp -private async Task WaitForToken(IDialogContext context, IAwaitable result) -{ - var activity = await result as Activity; - - var tokenResponse = activity.ReadTokenResponseContent(); - if (tokenResponse != null) - { - // Use the token to do exciting things! - } - else - { - if (!string.IsNullOrEmpty(activity.Text)) - { - tokenResponse = await context.GetUserTokenAsync(ConnectionName, - activity.Text); - if (tokenResponse != null) - { - // Use the token to do exciting things! - return; - } - } - await context.PostAsync($"Hmm. Something went wrong. Let's try again."); - await SendOAuthCardAsync(context, activity); - } -} -``` - -### Message controller - -On subsequent calls to the bot, notice that the token is never cached by this sample bot. This is because the bot can always ask the Azure Bot Service for the token. This avoids the bot needing to manage the token life-cycle, refresh the token, etc, as Azure Bot Service does all of this for you. - -```csharp -else if(message.Type == ActivityTypes.Event) -{ - if(message.IsTokenResponseEvent()) - { - await Conversation.SendAsync(message, () => new Dialogs.RootDialog()); - } -} -``` -## Additional resources -[Bot Framework SDK](https://github.com/microsoft/botbuilder) diff --git a/articles/bot-overview.md b/articles/bot-overview.md new file mode 100644 index 000000000..6f5380c66 --- /dev/null +++ b/articles/bot-overview.md @@ -0,0 +1,89 @@ +--- +title: Choose the right chatbot solution for your use case +description: Learn about different chatbot solutions, who they're for, and when to use them. +author: iaanw +ms.author: iawilt +manager: leeclontz +ms.reviewer: micchow +ms.topic: overview +ms.service: azure-ai-bot-service +ms.custom: + - mode-api + - tab-zone-seo + - evergreen +--- + +# Choose the right chatbot solution for your use case + +A copilot is an application that has written or spoken natural language as its user interface. In other words, a conversation is the means through which questions are answered, requests are serviced, and so on. + +This article provides an overview of some of the chatbot solutions Microsoft provides. If you're new to making chatbots, we recommend starting with Copilot Studio. + +The following table lists Microsoft products and services for building bots, who they support, and a brief description. Following sections describe each product in more detail. + +| Product | Audience | Description | +|:-|:-|:-| +| [Copilot Studio](#copilot-studio) | Fusion teams, citizen developers | Copilot Studio is an end-to-end copilot-building tool, with built-in natural language understanding models, data connectivity through Power Automate, and support for multiple channels. | +| [Health Bot](#health-bot) | Healthcare organizations | Provides support for healthcare organizations. Health Bot helps you build and deploy compliant, AI-powered virtual health assistants and health bots. | +| [Bot Framework SDK](#bot-framework-sdk) | Developers | Provides a framework for building bots, including tools, templates, and related AI services. The SDK is ideal for developers who want to build bots that are publicly available on the Microsoft Teams app store. | + +## Copilot Studio + +Copilot Studio is designed to support _fusion teams_—where professional developers and various subject matter experts collaborate. It also supports citizen developers and specialized vendors. + +Copilot Studio is a tool for chatbot development that's included in [Microsoft Power Platform](https://powerplatform.microsoft.com/)—a business-application platform that incorporates data analysis, solution building, and process automation. +You don't need to write code or understand the details of the underlying AI technologies to build bots in Copilot Studio. +Such bots can apply automation and other capabilities within the Power Platform, and you can rapidly develop sophisticated chatbot experiences. + +- You can connect virtual agents to various user platforms, such as [Microsoft 365](https://www.microsoft.com/microsoft-365) and [Microsoft Dynamics 365](https://dynamics.microsoft.com/). +- You can use over 600 prebuilt data connectors, available through Power Automate. + +For more information about Copilot Studio, see the [product overview page](https://www.microsoft.com/microsoft-copilot/microsoft-copilot-studio/). For details about pricing, see [Copilot Studio pricing](https://www.microsoft.com/microsoft-copilot/microsoft-copilot-studio/#Pricing). + +## Health Bot + +The Health Bot Service is a cloud platform that healthcare organizations can use to build and deploy compliant, AI-powered virtual health assistants and health bots. The service can help organizations improve processes and reduce costs. It offers your users _intelligent_ and _personalized_ access to health-related information and interactions through a natural conversation experience. + +The Health Bot Service is ideal for developers in IT departments of healthcare organizations such as providers, pharmaceutical companies, telemedicine providers, and health insurers. Healthcare organizations can use the service to build a _health bot instance_ and integrate it with their systems that patients, providers, and other representatives interact with. + +The Health Bot Service contains a built-in medical database, including triage protocols. You can also extend a health bot instance to include your own scenarios and integrate with other IT systems and data sources. + +For more information about the Health Bot Service, see [Health Bot Overview](/azure/health-bot/overview). For information about pricing models, see [Choosing the right Health Bot plan](/azure/health-bot/pricing-details). + +## Bot Framework SDK + +Microsoft Bot Framework and Azure AI Bot Service provide tools to build, test, deploy, and manage intelligent bots. The Bot Framework includes a modular and extensible SDK for building bots, including tools, templates, and related AI services. With this framework, developers can create bots that use speech, understand natural language, handle questions and answers, and more. + +The SDK is ideal for developers who want to build bots that are publicly available on the Microsoft Teams app store. + +Azure AI Bot Service and the Bot Framework offer: + +- The Bot Framework SDK for developing bots. +- Bot Framework tools to cover end-to-end bot development workflow. +- The Bot Connector service to send and receive messages and events between bots and channels. +- Bot deployment and channel configuration in Azure. + +Additionally, bots may make use of other Azure services: + +- Azure AI services to build intelligent applications. +- Azure Storage for cloud storage. + +For more information about the Bot Framework SDK, see [What is the Bot Framework SDK](bot-service-overview.md). +Once you have created and deployed your bot to Azure, the chatbot service consumes resources. +For details about pricing, see [Azure AI Bot Service pricing](https://azure.microsoft.com/pricing/details/bot-services/). +Costs associated with the consumption of resources on Azure are in addition to the cost of the chatbot service itself. + +## Skill bots + +As an advanced scenario, you can create a _skill_ bot that provides features to other bots. You can develop the skill bot and the bot that _consumes_ the skill in different products. For more information about skill bots, see: + +- [About skills in the SDK](./v4sdk/skills-conceptual.md) +- [Use a Copilot Studio copilot as a skill](/microsoft-copilot-studio/advanced-use-pva-as-a-skill) +- [Configure a skill for use in Copilot Studio](/microsoft-copilot-studio/configuration-add-skills) +- [Implement a skill with the Bot Framework SDK](./v4sdk/skill-implement-skill.md) + +## Next Steps + +- [Create and deploy a Copilot Studio copilot online](/microsoft-copilot-studio/fundamentals-get-started) +- [Create your first Health Bot](/azure/health-bot/quickstart-createyourhealthcarebot) +- [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md) diff --git a/articles/bot-service-activities-entities.md b/articles/bot-service-activities-entities.md index 38a1c1bb4..a3352ca7f 100644 --- a/articles/bot-service-activities-entities.md +++ b/articles/bot-service-activities-entities.md @@ -1,48 +1,67 @@ --- -title: Entities and activity types - Bot Service -description: Entities and activity types. +title: Entities and activity types in Azure AI Bot Service +description: Learn how entities store information that bots and channels use when exchanging messages. See how to populate entity properties and how to consume entities. keywords: mention entities, activity types, consume entities -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 03/01/2018 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - code-snippets + - evergreen --- + # Entities and activity types +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + Entities are a part of an activity, and provide additional information about the activity or conversation. -[!include[Entity boilerplate](includes/snippet-entity-boilerplate.md)] +> [!NOTE] +> Different parts of the SDK define separate _entity_ classes or elements. ## Entities -The *entities* property of a message is an array of open-ended schema.org -objects which allows the exchange of common contextual metadata between the channel and bot. +The _entities_ property of a message is an array of open-ended [schema.org](https://schema.org/) objects, which allows the exchange of common contextual metadata between the channel and bot. ### Mention entities Many channels support the ability for a bot or user to "mention" someone within the context of a conversation. -To mention a user in a message, populate the message's entities property with a *mention* object. +To mention a user in a message, populate the message's entities property with a _mention_ object. The mention object contains these properties: -| Property | Description | -|----|----| -| Type | type of the entity ("mention") | -| Mentioned | channel account object that indicates which user was mentioned | -| Text | text within the *activity.text* property that represents the mention itself (may be empty or null) | +| Property | Description | +|-----------|----------------------------------------------------------------------------------------------------| +| Type | Type of the entity ("mention") | +| Mentioned | Channel account object that indicates which user was mentioned | +| Text | Text within the _activity.text_ property that represents the mention itself (may be empty or null) | This code example shows how to add a mention entity to the entities collection. -# [C#](#tab/cs) -[!code-csharp[set Mention](includes/code/dotnet-create-messages.cs#setMention)] +### [C#](#tab/cs) + +```csharp +var entity = new Entity(); +entity.SetAs(new Mention() +{ + Text = "@johndoe", + Mentioned = new ChannelAccount() + { + Name = "John Doe", + Id = "UV341235" + } +}); +entities.Add(entity); +``` > [!TIP] > When attempting to determine user intent, the bot may want to ignore that portion -> of the message where it is mentioned. Call the `GetMentions` method and evaluate +> of the message where it's mentioned. Call the `GetMentions` method and evaluate > the `Mention` objects returned in the response. +### [JavaScript](#tab/js) -# [JavaScript](#tab/js) ```javascript var entity = context.activity.entities; @@ -62,36 +81,51 @@ entity = [mention]; ### Place objects -Location-related information can be conveyed +[Location-related information](https://schema.org/Place) can be conveyed within a message by populating the message's entities property with either -a *Place* object or a *GeoCoordinates* object. +a _Place_ object or a _GeoCoordinates_ object. The place object contains these properties: -| Property | Description | -|----|----| -| Type | type of the entity ("Place") | -| Address | description or postal address object (future) | -| Geo | GeoCoordinates | -| HasMap | URL to a map or map object (future) | -| Name | name of the place | +| Property | Description | +|----------|-----------------------------------------------| +| Type | Type of the entity ("Place") | +| Address | Description or postal address object (future) | +| Geo | GeoCoordinates | +| HasMap | URL to a map or map object (future) | +| Name | Name of the place | The geoCoordinates object contains these properties: -| Property | Description | -|----|----| -| Type | type of the entity ("GeoCoordinates") | -| Name | name of the place | -| Longitude | longitude of the location (WGS 84) | -| Latitude | latitude of the location (WGS 84) | -| Elevation | elevation of the location (WGS 84) | +| Property | Description | +|-----------|----------------------------------------| +| Type | Type of the entity ("GeoCoordinates") | +| Name | Name of the place | +| Longitude | Longitude of the location ([WGS 84][]) | +| Latitude | Latitude of the location ([WGS 84][]) | +| Elevation | Elevation of the location ([WGS 84][]) | + +[WGS 84]: https://gisgeography.com/wgs84-world-geodetic-system/ This code example shows how to add a place entity to the entities collection: -# [C#](#tab/cs) -[!code-csharp[set GeoCoordinates](includes/code/dotnet-create-messages.cs#setGeoCoord)] +### [C#](#tab/cs) + +```csharp +var entity = new Entity(); +entity.SetAs(new Place() +{ + Geo = new GeoCoordinates() + { + Latitude = 32.4141, + Longitude = 43.1123123, + } +}); +entities.Add(entity); +``` + +### [JavaScript](#tab/js) -# [JavaScript](#tab/js) ```javascript var entity = context.activity.entities; @@ -111,19 +145,34 @@ entity = [place]; ### Consume entities -# [C#](#tab/cs) +### [C#](#tab/cs) -To consume entities, use either the `dynamic` keyword or strongly-typed classes. +To consume entities, use either the `dynamic` keyword or strongly typed classes. This code example shows how to use the `dynamic` keyword to process an entity within the `Entities` property of a message: -[!code-csharp[examine entity using dynamic keyword](includes/code/dotnet-create-messages.cs#examineEntity1)] +```csharp +if (entity.Type == "Place") +{ + dynamic place = entity.Properties; + if (place.geo.latitude > 34) + // do something +} +``` -This code example shows how to use a strongly-typed class to process an entity within the `Entities` property of a message: +This code example shows how to use a strongly typed class to process an entity within the `Entities` property of a message: -[!code-csharp[examine entity using typed class](includes/code/dotnet-create-messages.cs#examineEntity2)] +```csharp +if (entity.Type == "Place") +{ + Place place = entity.GetAs(); + GeoCoordinates geo = place.Geo.ToObject(); + if (geo.Latitude > 34) + // do something +} +``` -# [JavaScript](#tab/js) +### [JavaScript](#tab/js) This code example shows how to process an entity within the `entity` property of a message: @@ -136,32 +185,5 @@ if (entity[0].type === "GeoCoordinates" && entity[0].latitude > 34) { --- ## Activity types - - -Activities can be of several different types past the most common **message**. Explanations and further details on different activity types can be found in the [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema). - -::: moniker range="azure-bot-service-3.0" - -## Additional resources -- Activity class -::: moniker-end +Activities can be of several different types past the most common **message**. Explanations and further details on different activity types can be found in the [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md). \ No newline at end of file diff --git a/articles/bot-service-adapter-connect-webex.md b/articles/bot-service-adapter-connect-webex.md deleted file mode 100644 index 67f88daaa..000000000 --- a/articles/bot-service-adapter-connect-webex.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Connect a bot to Webex Teams | Microsoft Docs -description: Learn how to configure a bot's connection to Webex via the Webex adapter. -keywords: bot adapter, Webex, Webex bot -author: garypretty -manager: kamrani -ms.topic: article -ms.author: gapretty -ms.service: bot-service -ms.date: 01/21/2020 ---- - -# Connect a bot to Webex Teams using the Webex adapter - -In this article you will learn how to connect a bot to Webex using the adapter available in the SDK. This article will walk you through modifying the EchoBot sample to connect it to a Webex app. - -> [!NOTE] -> The instructions below cover the C# implementation of the Webex adapter. For instructions on using the JavaScript implementation, part of the BotKit libraries, [see the BotKit Webex documentation](https://botkit.ai/docs/v4/platforms/webex.html). - -## Prerequisites - -* The [EchoBot sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/02.echo-bot) - -* Access to a Webex team with sufficient permissions to login to create / manage applications at [https://developer.webex.com/my-apps](https://developer.webex.com/my-apps). If you do not have access to a Webex team you can create an account for free at [www.webex.com](https://www.webex.com). - -## Create a Webex bot app - -1. Log into the [Webex developer dashboard](https://developer.webex.com/my-apps) and then click the 'Create a new app' button. - -2. On the next screen choose to create a Webex Bot by clicking 'Create a bot'. - -3. On the next screen, enter an appropriate name, username and description for your bot, as well as choosing an icon or uploading an image of your own. - - ![Set up bot](~/media/bot-service-adapter-connect-webex/create-bot.png) - - Click the 'Add bot' button. - -4. On the next page you will be provided with an access token for your new Webex app, please make a note of this token as you will require it when configuring your bot. - - ![Set up bot](~/media/bot-service-adapter-connect-webex/create-bot-settings.png) - -## Wiring up the Webex adapter in your bot - -Before you can complete the configuration of our Webex app, you need to wire up the Webex adapter into your bot. - -### Install the Webex adapter NuGet package - -Add the [Microsoft.Bot.Builder.Adapters.Webex](https://www.nuget.org/packages/Microsoft.Bot.Builder.Adapters.Webex/) NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs) - -### Create a Webex adapter class - -Create a new class that inherits from the ***WebexAdapter*** class. This class will act as our adapter for the Webex channel. It includes error handling capabilities (much like the ***BotFrameworkAdapterWithErrorHandler*** class already in the sample, used for handling requests from Azure Bot Service). - -```csharp -public class WebexAdapterWithErrorHandler : WebexAdapter -{ - public WebexAdapterWithErrorHandler(IConfiguration configuration, ILogger logger) - : base(configuration, logger) - { - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); - - // Send a message to the user - await turnContext.SendActivityAsync("The bot encountered an error or bug."); - await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); - - // Send a trace activity, which will be displayed in the Bot Framework Emulator - await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); - }; - } -} -``` - -### Create a new controller for handling Webex requests - -We create a new controller which will handle requests from your Webex app, on a new endpoint 'api/webex' instead of the default 'api/messages' used for requests from Azure Bot Service Channels. By adding an additional endpoint to your bot, you can accept requests from Bot Service channels (or additional adapters), as well as from Webex, using the same bot. - -```csharp -[Route("api/webex")] -[ApiController] -public class WebexController : ControllerBase -{ - private readonly WebexAdapter _adapter; - private readonly IBot _bot; - - public WebexController(WebexAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } - - [HttpPost] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _adapter.ProcessAsync(Request, Response, _bot); - } -} -``` - -### Inject Webex Adapter In Your Bot Startup.cs - -Add the following line into the ***ConfigureServices*** method within your Startup.cs file, which will register your Webex adapter and make it available for your new controller class. The configuration settings, described in the next step, will be automatically used by the adapter. - -```csharp -services.AddSingleton(); -``` - -Once added, your ***ConfigureServices*** method shold look like this. - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - // Create the default Bot Framework Adapter (used for Azure Bot Service channels and emulator). - services.AddSingleton(); - - // Create the Webex Adapter - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -### Add Webex adapter settings to your bot's configuration file - -1. Add the 4 settings shown below to your appSettings.json file in your bot project. - - ```json - "WebexAccessToken": "", - "WebexPublicAddress": "", - "WebexSecret": "", - "WebexWebhookName": "" - ``` - -2. Populate the **WebexAccessToken** setting within the Webex Bot Access Token, that was generated when creating your Webex bot app in the earlier steps. Leave the other 3 settings empty at this time, until we gather the information needed for them in later steps. - -## Complete configuration of your Webex app and bot - -### Create and update a Webex webhook - -Now that you have created a Webex app and wired up the adapter in your bot project, the final steps are to configure a Webex webhook, point it to the correct endpoint on your bot, and subscribe your app to ensure your bot receives messages and attachments. To do this your bot must be running, so that Webex can verify the URL to the endpoint is valid. - -1. To complete this step, [deploy your bot to Azure](https://aka.ms/bot-builder-deploy-az-cli) and make a note of the URL to your deployed bot. Your Webex messaging endpoint is the URL for your bot, which will be the URL of your deployed application (or ngrok endpoint), plus '/api/webex' (for example, `https://yourbotapp.azurewebsites.net/api/webex`). - - > [!NOTE] - > If you are not ready to deploy your bot to Azure, or wish to debug your bot when using the Webex adapter, you can use a tool such as [ngrok](https://www.ngrok.com) (which you will likely already have installed if you have used the Bot Framework emulator previously) to tunnel through to your bot running locally and provide you with a publicly accessible URL for this. - > - > If you wish create an ngrok tunnel and obtain a URL to your bot, use the following command in a terminal window (this assumes your local bot is running on port 3978, alter the port numbers in the command if your bot is not). - > - > ```cmd - > ngrok.exe http 3978 -host-header="localhost:3978" - > ``` - -2. Navigate to [https://developer.webex.com/docs/api/v1/webhooks](https://developer.webex.com/docs/api/v1/webhooks). - -3. Click the link for the PUT method `https://api.ciscospark.com/v1/webhooks/{webhookId}` (with the description "Update a Webhook"). This will expand a form allowing you to send a request to the endpoint. - - ![Set up bot](~/media/bot-service-adapter-connect-webex/webex-webhook-put-endpoint.png) - -4. Populate the form with the following details; - - * Name (provide a name for your webhook, such as "Messages Webhook") - * Target URL (the full URL to your bot's Webex endpoint, such as `https://yourbotapp.azurewebsites.net/api/webex`) - * Secret (here you should provide a secret of your choice to secure your webhook) - * Status (leave this as the default setting of 'active') - - ![Set up bot](~/media/bot-service-adapter-connect-webex/webex-webhook-form.png) - -5. Click **Run**, which should create your webhook and provide you with a success message. - -### Complete the remaining settings in your bot application - -Complete the remaining 3 settings in your bot's appsettings.json file (you already populated **WebexAccessToken** in an earlier step). - -* WebexPublicAddress (the full URL to your bot's Webex endpoint) -* WebexSecret (the secret you provided when creating your webhook in the previous step) -* WebexWebhookName (the name for your webhook you provided in the previous step) - -## Re-deploy your bot in your Webex team - -Now that you have completed the configuration of your bot's settings in appsettings.json, you should re-deploy your bot (or restart your bot if you are tunnelling to a local endpoint using ngrok). Configuration of you Webex app and bot are now complete. -You can now login to your Webex team at [https://www.webex.com](https://www.webex.com) and chat with your bot by sending it a message, in the same way you would contact another person. - -![Set up bot](~/media/bot-service-adapter-connect-webex/webex-contact-person.png) diff --git a/articles/bot-service-build-continuous-deployment.md b/articles/bot-service-build-continuous-deployment.md index 4d5dc9e91..a7fe38fcf 100644 --- a/articles/bot-service-build-continuous-deployment.md +++ b/articles/bot-service-build-continuous-deployment.md @@ -1,70 +1,81 @@ --- -title: Configure continuous deployment for Bot Service - Bot Service -description: Learn how to setup continuous deployment from source control for a Bot Service. +title: Configure continuous deployment +description: Learn how to set up continuous deployment from source control for a Bot Service. keywords: continuous deployment, publish, deploy, azure portal -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/10/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Set up continuous deployment -[!INCLUDE [applies-to](./includes/applies-to.md)] +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -This article shows you how to configure continuous deployment for your bot. You can enable continuous deployment to automatically deploy code changes from your source repository to Azure. In this topic, we'll cover setting up continuous deployment for GitHub. For information on setting up continuous deployment with other source control systems, see the additional resource section at the bottom of this page. +This article describes how to configure continuous deployment. You can enable continuous deployment to automatically deploy code changes from your source repository to Azure. -## Prerequisites -- If you don't have an Azure subscription, create a [free account](https://portal.azure.com) before you begin. -- You **must** [deploy your bot to Azure](bot-builder-deploy-az-cli.md) before enabling continuous deployment. - -## Prepare your repository -Make sure that your repository root has the correct files in your project. This will allow you to get automatic builds from the Azure App Service Kudu build server. - -|Runtime | Root directory files | -|:-------|:---------------------| -| ASP.NET Core | .sln or .csproj | -| Node.js | server.js, app.js, or package.json with a start script | -| Python | app.py | +This article covers setting up continuous deployment for GitHub. For information on setting up continuous deployment with other source control systems, see [continuous deployment to Azure App Service](/azure/app-service/deploy-continuous-deployment). +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] -## Continuous deployment using GitHub -To enable continuous deployment with GitHub, navigate to the **App Service** page for your bot in the Azure portal. +## Prerequisites -Click **Deployment Center** > **GitHub** > **Authorize**. +- If you don't have an Azure subscription, create a [free account](https://portal.azure.com) before you begin. +- Before setting up continuous deployment, [Deploy your bot to Azure](bot-builder-deploy-az-cli.md) _at least once_. +- A GitHub account and a repository to use for your bot. -![Continous deployment](~/media/azure-bot-build/azure-deployment.png) +## Prepare your GitHub repository -In the browser window that opens up, click **Authorize AzureAppService**. +Add your bot project to your GitHub repository. -![Azure Github Permission](~/media/azure-bot-build/azure-deployment-github.png) +> [!IMPORTANT] +> To enable automatic builds from the build provider, your _repository root_ must contain specific files for your project. +> +> | Runtime | Root directory files | +> |:-------------|:-------------------------------------------------------------------| +> | ASP.NET Core | **.sln** or **.csproj** | +> | Node.js | **server.js**, **app.js**, or **package.json** with a start script | +> | Java | **pom.xml** | +> | Python | **app.py** | -After authorizing the **AzureAppService**, go back to **Deployment Center** in the Azure portal. +## Set up continuous deployment with GitHub -1. Click **Continue**. +1. Go to the [Azure portal](https://portal.azure.com/). +1. Open the **App Service** blade for your bot. +1. Under **Deployment**, select **Deployment Center** to open the **Deployment Center** blade. +1. Select the **Settings** tab. + 1. For **Source**, select **GitHub**. + 1. Change the build provider: + 1. Select **Change provider**. + 1. Select **App Service Build Service**, then **OK**. -1. Select **App Service build service**. + 1. If you haven't connected to GitHub from Azure before, select **Authorize** to authorize Azure App Service to access your GitHub account. + 1. Check that the **Signed in as** field shows your correct GitHub account. -1. Click **Continue**. + To sign into and authorize a different account, select **Change account**. -1. Select **Organization**, **Repository**, and **Branch**. + 1. For **Organization**, **Repository**, and **Branch**, select the GitHub organization, repository, and branch that contains your bot project. + 1. Select **Save**. -1. Click **Continue**, and then **Finish** to complete the setup. +At this point, continuous deployment with GitHub is set up. New commits in the selected repository and branch now deploy continuously into your App Service app. You can track the commits and deployments on the **Logs** tab. -At this point, continuous deployment with GitHub is set up. Whenever you commit to the source code repository, your changes will automatically be deployed to the Azure Bot Service. +:::image type="content" source="media/bot-service-build-continuous-deployment/cicd-configured.png" alt-text="Screenshot of the Deployment Center blade, with the source and build provider configured."::: ## Disable continuous deployment While your bot is configured for continuous deployment, you may not use the online code editor to make changes to your bot. If you want to use the online code editor, you can temporarily disable continuous deployment. -To disable continuous deployment, do the following: -1. In the [Azure portal](https://portal.azure.com), go to your bot's **All App Service settings** blade and click **Deployment Center**. -1. Click **Disconnect** to disable continuous deployment. To re-enable continuous deployment, repeat the steps from the appropriate sections above. - -## Additional resources -- To enable continuous deployment from BitBucket and Azure DevOps Services, see [continous deployment using Azure App Service](https://docs.microsoft.com/azure/app-service/deploy-continuous-deployment). +To disable continuous deployment: +1. Go to the [Azure portal](https://portal.azure.com/). +1. Open the **App Service** blade for your bot. +1. Under **Deployment**, select **Deployment Center** to open the **Deployment Center** blade. +1. Select the **Settings** tab. +1. Select **Disconnect** to disable continuous deployment. +To re-enable continuous deployment, repeat the steps from [Set up continuous deployment with GitHub](#set-up-continuous-deployment-with-github). diff --git a/articles/bot-service-channel-additional-channels.md b/articles/bot-service-channel-additional-channels.md index a35a7cc2d..c5885f25f 100644 --- a/articles/bot-service-channel-additional-channels.md +++ b/articles/bot-service-channel-additional-channels.md @@ -1,37 +1,45 @@ --- -title: Additional channels - Bot Service -description: Learn how to configure additional channels for your bot. +title: Additional channels in Bot Framework SDK +description: Learn about the various modalities to access bots via channels; specifically channel adapters and Azure channels. keywords: bot channels, hangouts, Twilio, facebook, azure portal -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/08/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Additional channels -Besides the channels listed in within these docs, there are a few additional channels available as an adapter, both through our [provided platforms](https://botkit.ai/docs/v4/platforms/) via Botkit, or accessible through the [community repositories](https://github.com/BotBuilderCommunity/). Below are the additional channels provided: +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -- [Webex Teams](https://botkit.ai/docs/v4/platforms/webex.html) -- [Websocket and Webhooks](https://botkit.ai/docs/v4/platforms/web.html) -- [Google Hangouts and Google Assistant](https://github.com/BotBuilderCommunity/) (available through community) -- [Amazon Alexa](https://github.com/BotBuilderCommunity/) (available through community) +You can make your bot accessible in channels in two ways: + +1. **Azure AI Bot Service channels**: make your bot available in channels with the Azure AI Bot Service. This works for all supported languages. +1. **Channel adapters**: make your bot available in channels with an adapter. The channel adapter translates between the Bot Framework activity schema and the native schema of a channel. The SDK, Botkit, and Bot Builder Community repos provide various channel adapters, which are language specific. + + 1. The Bot Framework SDK repo lists many of the [available adapters](https://github.com/microsoft/botframework-sdk#channels-and-adapters), including Azure AI Bot Service channels and channel adapters. + 1. The Botkit repo includes channel adapters, which they call [platform adapters](https://github.com/howdyai/botkit/blob/main/packages/docs/platforms/index.md). Botkit is an open source developer tool for building chat bots, apps and custom integrations for major messaging platforms. + 1. The [Bot Builder Community](https://github.com/BotBuilderCommunity/) repositories include channel adapters. View the README for each repo to see which channel adapters have been developed. + +Some channels are accessible through Azure AI Bot Service or through an adapter. It's up to you when to use a channel versus an adapter. ## Currently available adapters -A complete list of available adapters can be [found here](https://botkit.ai/docs/v4/platforms/). You'll notice some channels are available also as an adapter, and it's up to you when to use a channel versus an adapter. +Each repository is responsible for maintaining the list of adapters and channels they support. -### When to use an adapter +## When to use a channel adapter -1. The service does not support the channel you want -2. Security and compliance requirements of your deployment dictate that you cannot rely on an outside service -3. Depth of features that you need in a particular channel may not be supported +1. Azure AI Bot Service doesn't support the channel you want. +1. Security and compliance requirements of your deployment dictate that you can't rely on an outside service. +1. Depth of features that you need in a particular channel may not be supported. -### When to use a channel +## When to use an Azure channel -1. You require cross-channel compatibility: your bot should work on more than one of the available channels -2. Built in support: Microsoft maintains, patches, and seamlessly services each channel for you each time a third-party makes updates -3. Allows access to additional exclusive Microsoft channels, like quickly growing Microsoft Teams -4. If you want to rely on a GUI interface to enable additional channels for your bot +1. You require cross-channel compatibility, such that your bot should work on more than one of the available channels. +1. Built-in support. Microsoft maintains, patches, and seamlessly services each channel for you each time a third-party makes updates. +1. You want access to additional exclusive Microsoft channels, like quickly growing Microsoft Teams. +1. If you want to rely on a GUI interface to enable additional channels for your bot. diff --git a/articles/bot-service-channel-azure-cli.md b/articles/bot-service-channel-azure-cli.md new file mode 100644 index 000000000..b39accc37 --- /dev/null +++ b/articles/bot-service-channel-azure-cli.md @@ -0,0 +1,171 @@ +--- +title: Connect your bot to channels with Azure CLI +description: This sample shows Azure CLI commands for connecting your bot to a communication application, such as email or Facebook. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - devx-track-azurecli + - evergreen +--- + +# Connect your bot to channels with Azure CLI + +A channel is a connection between a communication application and a bot. A bot, registered with Azure, uses channels to enable communication with users. The commands in this article connect a bot to various channels. For more information, see [Connect a bot to channels](bot-service-manage-channels.md). + +[!INCLUDE [include](~/../articles/reusable-content/azure-cli/azure-cli-prepare-your-environment.md)] + +- A bot deployed to Azure. If you don't have one yet, see [Tutorial: Provision a bot in Azure](tutorial-provision-a-bot.md) and [Tutorial: Publish a basic bot](tutorial-publish-a-bot.md). + +## Sample commands + +The following sections use Azure CLI commands to connect a bot to a channel. These examples use a bot named `ContosoBot` in the `ContosoBotRG` resource group. + +Some of these channels require the command to connect with the application to authenticate. If you're running these commands for testing purposes, they can fail if you don't use real values. + +### Direct Line + +Direct Line integrates your bot into a mobile app, web page, or other applications. For more information, see [About Direct Line](bot-service-channel-directline.md). + +These sample commands create a connection to the Direct Line channel by using [az bot directline create](/cli/azure/bot/directline#az-bot-directline-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot directline create --resource-group ContosoBotRG --name ContosoBot --disablev1 +az bot directline show --resource-group ContosoBotRG --name ContosoBot +az bot directline delete --resource-group ContosoBotRG --name ContosoBot +``` + +### Office 365 email + +You can enable your bot to communicate with users by using Office 365 email. For more information, see [Connect a bot to Office 365 email](bot-service-channel-connect-email.md). + +These sample commands create a connection to the channel for Office 365 email by using [az bot email create](/cli/azure/bot/email#az-bot-email-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot email create --resource-group ContosoBotRG --name ContosoBot \ + --email-address ContosoBot@outlook.com --password +az bot email show --resource-group ContosoBotRG --name ContosoBot +az bot email delete --resource-group ContosoBotRG --name ContosoBot +``` + +### Facebook + +You can connect your bot to both Facebook Messenger and Facebook Workplace. It can communicate with users on both platforms. For more information, see [Connect a bot to Facebook](bot-service-channel-connect-facebook.md). + +These sample commands create a connection to the channel for Facebook by using [az bot facebook create](/cli/azure/bot/facebook#az-bot-facebook-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot facebook create --resource-group ContosoBotRG --name ContosoBot --appid \ + --page-id --secret --token +az bot facebook show --resource-group ContosoBotRG --name ContosoBot +az bot facebook delete --resource-group ContosoBotRG --name ContosoBot +``` + +### Microsoft Teams + +You can configure your bot to communicate with Microsoft Teams. For more information, see [Connect a bot to Microsoft Teams](channel-connect-teams.md). + +These sample commands create a connection to the channel for Microsoft Teams by using [az bot msteams create](/cli/azure/bot/msteams#az-bot-msteams-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot msteams create --resource-group ContosoBotRG --name ContosoBot --calling-web-hook https://www.contosoapp.com/ \ + --enable-calling +az bot msteams show --resource-group ContosoBotRG --name ContosoBot +az bot msteams delete --resource-group ContosoBotRG --name ContosoBot +``` + +### Skype + +You can configure your bot to communicate with Skype. For more information, see [Connect a bot to Skype](bot-service-channel-connect-skype.md). + +These sample commands create a connection to the channel for Skype by using [az bot skype create](/cli/azure/bot/skype#az-bot-skype-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot skype create --resource-group ContosoBotRG --name ContosoBot --enable-messaging --enable-screen-sharing +az bot skype show --resource-group ContosoBotRG --name ContosoBot +az bot skype delete --resource-group ContosoBotRG --name ContosoBot +``` + +### Slack + +You can configure your bot to communicate with users through Slack. For more information, see [Connect a bot to Slack](bot-service-channel-connect-slack.md). + +These sample commands create a connection to the channel for Slack by using [az bot slack create](/cli/azure/bot/slack#az-bot-slack-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot slack create --resource-group ContosoBotRG --name ContosoBot --client-id \ + --client-secret --verification-token +az bot slack show --resource-group ContosoBotRG --name ContosoBot +az bot slack delete --resource-group ContosoBotRG --name ContosoBot +``` + +### SMS + +These sample commands create a connection to the channel for SMS by using [az bot sms create](/cli/azure/bot/sms#az-bot-sms-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot sms create --resource-group ContosoBotRG --name ContosoBot --account-sid --auth-token \ + --phone --is-validated +az bot sms show --resource-group BotRG +az bot sms delete --resource-group BotRG +``` + +### Telegram + +You can configure your bot to communicate with users through Telegram. For more information, see [Connect a bot to Telegram](bot-service-channel-connect-telegram.md). + +These sample commands create a connection to the channel for Telegram by using [az bot telegram create](/cli/azure/bot/telegram#az-bot-telegram-create). The example shows the connection in the console and deletes the connection. + +```azurecli +az bot telegram create --resource-group ContosoBotRG --name ContosoBot --access-token --is-validated +az bot telegram show --resource-group ContosoBotRG --name ContosoBot +az bot telegram delete --resource-group ContosoBotRG --name ContosoBot +``` + +## Clean up deployment + +If you created a resource group for testing, run the [az group delete](/cli/azure/group#az-group-delete) command to remove the resource group and everything it contains. + +```azurecli +az group delete --name ContosoBotRG +``` + +To remove a connection to a channel, use the appropriate delete command. + +## Azure CLI commands used in this article + +This article uses the following Azure CLI commands: + +- [az bot directline create](/cli/azure/bot/directline#az-bot-directline-create) +- [az bot directline delete](/cli/azure/bot/directline#az-bot-directline-delete) +- [az bot directline show](/cli/azure/bot/directline#az-bot-directline-show) +- [az bot email create](/cli/azure/bot/email#az-bot-email-create) +- [az bot email delete](/cli/azure/bot/email#az-bot-email-delete) +- [az bot email show](/cli/azure/bot/email#az-bot-email-show) +- [az bot facebook create](/cli/azure/bot/facebook#az-bot-facebook-create) +- [az bot facebook delete](/cli/azure/bot/facebook#az-bot-facebook-delete) +- [az bot facebook show](/cli/azure/bot/facebook#az-bot-facebook-show) +- [az bot msteams create](/cli/azure/bot/msteams#az-bot-msteams-create) +- [az bot msteams delete](/cli/azure/bot/msteams#az-bot-msteams-delete) +- [az bot msteams show](/cli/azure/bot/msteams#az-bot-msteams-show) +- [az bot skype create](/cli/azure/bot/skype#az-bot-skype-create) +- [az bot skype delete](/cli/azure/bot/skype#az-bot-skype-delete) +- [az bot skype show](/cli/azure/bot/skype#az-bot-skype-show) +- [az bot slack create](/cli/azure/bot/slack#az-bot-slack-create) +- [az bot slack delete](/cli/azure/bot/slack#az-bot-slack-delete) +- [az bot slack show](/cli/azure/bot/slack#az-bot-slack-show) +- [az bot sms create](/cli/azure/bot/sms#az-bot-sms-create) +- [az bot sms delete](/cli/azure/bot/sms#az-bot-sms-delete) +- [az bot sms show](/cli/azure/bot/sms#az-bot-sms-show) +- [az bot telegram create](/cli/azure/bot/telegram#az-bot-telegram-create) +- [az bot telegram delete](/cli/azure/bot/telegram#az-bot-telegram-delete) +- [az bot telegram show](/cli/azure/bot/telegram#az-bot-telegram-show) +- [az group delete](/cli/azure/group#az-group-delete) + +## Next steps + +- [Connect a bot to channels](bot-service-manage-channels.md) +- [Manage a bot](bot-service-manage-overview.md) diff --git a/articles/bot-service-channel-azure-communication.md b/articles/bot-service-channel-azure-communication.md new file mode 100644 index 000000000..bc1e5188e --- /dev/null +++ b/articles/bot-service-channel-azure-communication.md @@ -0,0 +1,52 @@ +--- +title: Connect a bot to Azure Communication Services +description: Learn how to configure Bot Framework bots to use Azure Communication Services to communicate with users. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +--- + +# Connect a bot to Azure Communication Services + +Azure Communication Services provides the APIs and SDKs to build multi-participant collaboration applications that can include a bot. This bot can greet users, perform intake tasks to determine the topic, and answer questions. Azure Communication Services allows you to hand off the conversation to a human agent. It also enables a supervisor or additional bot to monitor or participate in the conversation if necessary. Additionally, Azure Communication Services offers a [UI library](https://azure.github.io/communication-ui-library) to easily build conversational user experiences that can be embedded into your existing web pages. + + +## Prerequisites + +- Knowledge of the [Basics of the Bot Framework Service](v4sdk/bot-builder-basics.md) and how to [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md). +- A published bot that you want to connect to the channel. +- An Azure account with a current subscription. If you don't have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An Azure Communication Services resource. If you don't have one, see [Create an Azure Communication Services resource](/azure/communication-services/quickstarts/create-communication-resource). +- .NET 6.0 or later. +- Visual Studio 2022 or later. + +## Connect your bot to Azure Communication Services + +1. In the Azure portal, go to your bot resource. +1. Open the **Channels** blade and select **Communication Services - Chat**. +1. On the Configure Communication blade, connect to your Azure Communication Services resource and save. For more information, see the Azure Communication Services documentation on how to [enable the Azure Communication Services channel](/azure/communication-services/quickstarts/chat/quickstart-botframework-integration#step-3---enable-azure-communication-services-chat-channel). + +Your bot is now registered with Azure Communication Services. + +## Create a chat app and add your bot as a participant + +Now that your bot is registered with Azure Communication Services, you can create a chat thread with your bot as a participant. +For more information, see [Add a bot to your chat app](/azure/communication-services/quickstarts/chat/quickstart-botframework-integration) in the Azure Communication Services documentation. + +## Additional information + +The Azure Communications Services channel has a limit of 28 KB for message activities. + +## Next steps + +- For information on how to hand off the conversation to a human agent, see [Transition conversations from bot to human](bot-service-design-pattern-handoff-human.md) for more information. +- For information about channel support in the Bot Connector Service, see [Connect a bot to channels](bot-service-manage-channels.md). +- For information about building bots, see [How bots work](v4sdk/bot-builder-basics.md) and the [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md) quickstart. +- For information about deploying bots, see [Deploy your bot](bot-builder-deploy-az-cli.md) and [Set up continuous deployment](bot-service-build-continuous-deployment.md). +- For information about [Azure Communication Services UI Library](https://azure.github.io/communication-ui-library) diff --git a/articles/bot-service-channel-connect-actionable-email.md b/articles/bot-service-channel-connect-actionable-email.md new file mode 100644 index 000000000..299d4815d --- /dev/null +++ b/articles/bot-service-channel-connect-actionable-email.md @@ -0,0 +1,92 @@ +--- +title: Connect to Outlook for Actionable Messages - Bot Service +description: Learn how to configure bots to send and receive actionable email messages by using Adaptive Cards to power your Outlook Actionable Messages. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +--- + +# Connect a bot to the Outlook channel for Actionable Messages (Preview) + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Actionable Messages allow users to take quick actions from within Outlook. When you connect a bot to the Outlook Actionable Messages channel, your bot can use Adaptive Cards to create actionable messages, enhancing user engagement. + +> [!NOTE] +> The Outlook channel for Actionable Messages is in public preview. Features included in preview might not be complete, and might undergo changes before becoming available in the public release. They're provided for evaluation and exploration purposes only. + +> [!WARNING] +> It's a violation of the Bot Framework [Code of Conduct](https://www.botframework.com/Content/Developer-Code-of-Conduct-for-Microsoft-Bot-Framework.htm) to create "spambots", including bots that send unwanted or unsolicited bulk email. + +## Prerequisites + +- Knowledge of [Adaptive Cards](/adaptive-cards) and the [Universal Action Model](/adaptive-cards/authoring-cards/universal-action-model). +- Familiarity with [Actionable messages in Outlook and Office 365 Groups](/outlook/actionable-messages/). +- A channel-ready bot deployed to Azure. +- A dedicated email address that your bot will use to send and receive messages. + +> [!NOTE] +> +> - Don't use your own personal email accounts for bots, as every message sent to that email account will be forwarded to the bot. This can result in the bot inappropriately sending a response to a sender. For this reason, bots should only use dedicated O365 email accounts. +> - If you use Microsoft Exchange Server, enable [Autodiscover](/exchange/client-developer/exchange-web-services/autodiscover-for-exchange) before configuring the email channel. + +## Design the messages + +The Outlook channel for Actionable Messages sends and receives Adaptive Cards over email. + +You can use the [Actionable Message Designer](https://amdesigner.azurewebsites.net/) to design and test actionable message cards. + +> [!IMPORTANT] +> The universal Bot action model is introduced in the Adaptive Cards schema version 1.4. To use these new capabilities, the version property of your Adaptive Card should be set to 1.4 or later. See [Action.Execute](https://adaptivecards.io/explorer/Action.Execute.html) in the Adaptive Cards Schema Explorer. + +## Implement and deploy the bot + +The general flow is for the bot to send an actionable message to a user and then handle the user's action. + +1. When the user responds to the card in email, Outlook sends an invoke activity to the bot. + The activity's `value` property contains an action object with `type`, `verb`, and `data` properties. For example, the activity would include the following information, where the _verb_ and the _data_ correspond to information on the card the bot sent out initially. + + ```json + "type": "invoke", + "name": "adaptiveCard/action", + "value": { + "action": { + "type": "Action.Execute", + "verb": "", + "data": { + // DEVELOPER_DEFINED_PROPERTIES + } + }, + } + ``` + + See [Action.Execute](https://adaptivecards.io/explorer/Action.Execute.html) in the Adaptive Cards Schema Explorer for the complete object schema. + +1. The bot handles the incoming invoke activity and returns a result that includes a new Adaptive Card that will take the place of the original Adaptive Card. The invoke response might look like: + + ```json + { + "statusCode": 200, + "type": "application/vnd.microsoft.card.adaptive", + "value": + } + ``` + +## Request access + +1. Open your bot resource in the [Azure portal](https://ms.portal.azure.com/). +1. Open the **Channels** pane. +1. Select the **Outlook** channel. +1. On the **Configure Outlook** page, select **please register here**. +1. Fill out the registration form to request access. See [Register your service with the actionable email developer dashboard](/outlook/actionable-messages/email-dev-dashboard) for more information. + +## Next steps + +- Learn more about [Actionable messages in Outlook and Office 365 Groups](/outlook/actionable-messages/). +- Learn more about [Adaptive Cards for Outlook Actionable Message Developers](/adaptive-cards/getting-started/outlook) diff --git a/articles/bot-service-channel-connect-alexa.md b/articles/bot-service-channel-connect-alexa.md new file mode 100644 index 000000000..7972940eb --- /dev/null +++ b/articles/bot-service-channel-connect-alexa.md @@ -0,0 +1,164 @@ +--- +title: Connect a bot to Alexa +description: Learn how to configure your bot in Azure to allow communication with Alexa. +keywords: connect a bot, bot channel, Alexa bot, credentials, configure, phone +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen +--- + +# Connect a bot to Alexa + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +You can configure your bot to communicate with people through an Alexa custom skill. This article describes how to create an Alexa skill using the Alexa Developer Console, connect your bot to your Alexa skill in Azure, and test your bot in Alexa. + +## Prerequisites + +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A bot published to Azure that you want to connect to Alexa. +- An Amazon account. + +> [!IMPORTANT] +> Your bot must use the [Bot Framework SDK](https://github.com/microsoft/botframework-sdk) version 4.8 or later. If you have an existing bot you may need to update your SDK version and republish your bot. + +## Create an Alexa skill + +1. Sign in to the [Alexa Developer Console](https://developer.amazon.com/alexa/console/ask) and select **Create Skill**. + +1. On the next page: + 1. Enter a name for your new skill. + 1. Make sure that **Choose a model to add to your skill** is set to **Custom**. + 1. Make sure that **Choose a method to host your skill's backend resources** is set to **Provision your own**. + 1. Select **Create Skill**. + + :::image type="content" source="media/channels/alexa-create-skill-options.png" alt-text="Choose model and hosting"::: + +1. On the next page: + 1. Make sure that **Choose a template** is set to **Start from scratch** + 1. Select **Choose**. + + :::image type="content" source="media/channels/alexa-create-skill-options2.png" alt-text="Choose a template"::: + +1. On your skill dashboard under **Interaction Model**, select **JSON Editor**. + +1. In the JSON editor: + 1. Replace the existing contents with the following JSON. + + ```json + { + "interactionModel": { + "languageModel": { + "invocationName": "", + "intents": [ + { + "name": "GetUserIntent", + "slots": [ + { + "name": "phrase", + "type": "phrase" + } + ], + "samples": [ + "{phrase}" + ] + }, + { + "name": "AMAZON.StopIntent", + "samples": [] + } + ], + "types": [ + { + "name": "phrase", + "values": [ + { + "name": { + "value": "" + } + }, + { + "name": { + "value": "" + } + }, + { + "name": { + "value": "" + } + } + ] + } + ] + } + } + } + ``` + + 1. For `invocationName`, change \ to the name that users will use to invoke your skill on Alexa. For example, if your skill invocation name was "adapter helper", then a user could say "Alexa, launch adapter helper" to launch the skill. + + 1. In the `values` array under `types`, replace the three instances of `` with phrases that users can say to trigger your skill. For example, if a user says "Alexa, ask adapter helper to give me details of the alexa adapter", one example phrase could be "give me details of the alexa adapter". + +1. Select **Save Model**, then select **Build Model**. This updates your skill's configuration on Alexa. + +## Configure your bot in Azure + +To complete this step, you'll need your Alexa Skill ID. Get the ID either from the URL in the Alexa portal or by going to the [Alexa Developer Console](https://developer.amazon.com/alexa/console/ask) and selecting **Copy Skill ID**. Your Alexa Skill ID should be a value like "amzn1.ask.skill.\". + +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **Alexa**. +1. In **Alexa Channel Configuration**, enter the information you copied in the previous step. + 1. In **Enter skill Id**, enter the Alexa skill ID that you copied from the Alexa Developer Console. + 1. Select **Apply**. + 1. Copy the Azure-generated Alexa service endpoint URI. + +## Update your Alexa bot registration + +1. Sign in to the [Alexa Developer Console](https://developer.amazon.com/alexa/console/ask). +1. Find and open your skill's configuration page. +1. Select **Endpoint**. +1. For **Service Endpoint Type**, select **HTTPS**. +1. For **Default Region**: + 1. Enter the Alexa service endpoint URI you copied from the Azure portal. + 1. In the drop-down, select **My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority**. + +1. Select **Save Endpoints**. + +## Test and publish your skill + +If you own an Alexa device, you can test your skill before you publish it. + +See the [Alexa Skills Kit developer documentation for custom voice model skills](https://developer.amazon.com/docs/alexa/custom-skills/understanding-custom-skills.html) for information on how to test and publish your skill on their platform. + +## Additional information + +For more information about Alexa skills, see the Amazon developer documentation: + +- [What is the Alexa Skills Kit?](https://developer.amazon.com/docs/alexa/ask-overviews/what-is-the-alexa-skills-kit.html) +- [Custom Voice Model Skills](https://developer.amazon.com/docs/alexa/custom-skills/understanding-custom-skills.html) +- [Alexa Skills Kit Object Schemas](https://developer.amazon.com/docs/alexa/smapi/object-schemas.html) + +### User authentication in Alexa + +User authentication in Alexa is done by setting up and using **Account Linking on the Alexa skill**. +For more information, see [Understand Account Linking for Alexa Skills](https://developer.amazon.com/docs/alexa/account-linking/understand-account-linking.html). +You can require account linking when the user enables the skill, or you can require it as part of a conversation flow. + +If you add user authentication as part of the conversation: + +1. Attach a [sign-in card](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md#signin-card) on the outgoing activity. This will be converted to an Alexa **LinkAccount card** that prompts the user to sign in using the Alexa app. + +1. If the user successfully links their account into the app, a token is then available on subsequent requests in the channel data. + +## Next steps + +- For information about building bots, see [How bots work](v4sdk/bot-builder-basics.md) and the [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md) quickstart. +- For information about deploying bots, see [Tutorial: Provision a bot in Azure](tutorial-provision-a-bot.md) and [Tutorial: Publish a basic bot](tutorial-publish-a-bot.md). +- For information about channel support in the Bot Connector Service, see [Connect a bot to channels](bot-service-manage-channels.md). +- For more information about the Bot Framework schemas, see the [Bot Framework activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) and the [Bot Framework cards schema](https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md). diff --git a/articles/bot-service-channel-connect-cortana.md b/articles/bot-service-channel-connect-cortana.md deleted file mode 100644 index 4ab4b43fd..000000000 --- a/articles/bot-service-channel-connect-cortana.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Connect a bot to Cortana - Bot Service -description: Learn how to configure a bot for access through the Cortana interface. -keywords: cortana, bot channel, configure cortana -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/22/2019 ---- -# Connect a bot to Cortana - -Cortana is a speech-enabled channel that can send and receive voice messages in addition to textual conversation. A bot intended to connect to Cortana should be designed for speech as well as text. A Cortana *skill* is a bot that can be invoked using a Cortana client. Publishing a bot adds the bot to the list of available skills. - -To add the Cortana channel, open the bot in the [Azure Portal](https://portal.azure.com/), click the **Channels** blade, and then click **Cortana**. - -![Add Cortana channel](~/media/channels/cortana-addchannel.png) - -## Configure Cortana - -When connecting your bot with the Cortana channel, some basic information about your bot will be pre-filled into the registration form. Review this information carefully. This form consists of the following fields. - -| Field | Description | -|------|------| -| **Skill icon** | An icon that is displayed in the Cortana canvas when your skill is invoked. This is also used where skills are discoverable (like the Microsoft store). (32KB max, PNG only).| -| **Display name** | The name of your Cortana skill is displayed to the user at the top of the visual UI. (30 character limit) | -| **Invocation name** | This is the name users say when invoking a skill. It should be no more than three words and easy to pronounce. See the [Invocation Name Guidelines][invocation] for more information on how to choose this name.| - -![Default settings](~/media/channels/cortana-defaultsettings.png) - ->!NOTE: Cortana does not currently support the use of Azure Active Directory (AAD) Account authentication. You will need to use a Microsoft Account (MSA) to successfully publish your bot to Cortana. - -## General bot information - -Under the **Manage user identity through connected services section** press the option to enable it. Fill in the form. - -All fields marked with an asterisk (*) are required. A Bot must be published to Azure before it can be connected to Cortana. - -![Manage user identity, part 1](~/media/channels/cortana-manageidentity-1.png) -![Manage user identity, part 2](~/media/channels/cortana-manageidentity-2.png) - -### When should Cortana prompt for a user to sign in - -Select **Sign in at invocation** if you want Cortana to sign in the user at the time they invoke your skill. - -Select **Sign in when required** if you use a Bot Service sign-in card to sign in the user. Typically, you use this option if you want to sign in the user only if they will use a feature that requires authentication. When your skill sends a message that includes the sign-in card as an attachment, Cortana ignores the sign-in card and performs the authorization flow using the Connect Account settings. - -### Account name - -The name of your skill that you want displayed when the user signs in to your skill. - -### Client ID for third-party services - -Your bot's application ID. You received the ID when you registered your bot. - -### Space-separated list of scopes - -Specify the scopes that the service requires (see the service's documentation). - -### Authorization URL - -Set to `https://login.microsoftonline.com/common/oauth2/v2.0/authorize`. - -### Token options - -Select **POST**. - -### Grant type - -Select **Authorization code** to use the code grant flow, or select **Implicit** to use the implicit flow. - -### Token URL - -For the **Authorization code** grant type, set to `https://login.microsoftonline.com/common/oauth2/v2.0/token`. - -### Client secret/password for third party services - -The bot's password. You received the password when you registered your bot. - -### Client authentication scheme - -Select **HTTP Basic**. - -### Internet access required to authenticate users - -Leave this unchecked. - -### Request user profile data (optional) - -Cortana provides access to several different types of user profile information, that you can use to customize the bot for the user. For example, if a skill has access to the user's name and location then the skill can have custom response such as "Hello Kamran, I hope you are having a pleasant day in Bellevue, Washington." - -Click **Add a user profile request**, then select the user profile information you want from the drop-down list. Add a friendly name to use to access this information from your bot's code. - -### Deploy on Cortana - -When you are done filling out the registration form for your Cortana skill, click **Deploy on Cortana** to complete the connection. This brings you back to your bot's Channels blade and you should see that it is now connected to Cortana. - -At this point your bot is deployed as a Cortana skill to your account. - -Next, you need to enable and authorize the Bot skill to connect to your account. In your Cortana app, say or type "ask *Invocation Name*" where the "Invocation Name" is what you configured in the Cortana Channel in the Azure portal. Cortana will then prompt you to allow your Bot Skill to connect. If you choose "Yes" to allow this, the skill will now work and show up in Cortana's skill list. - -## Next steps - -* [Cortana Skills Kit](https://aka.ms/CortanaSkillsKitOverview) -* [Enable debugging](bot-service-debug-cortana-skill.md) -* [Publish a Cortana skill][publish] - -[invocation]: https://docs.microsoft.com/cortana/skills/cortana-invocation-guidelines -[publish]: https://docs.microsoft.com/cortana/skills/publish-skill -[CortanaEntity]: https://aka.ms/cortana-channel-data diff --git a/articles/bot-service-channel-connect-directline.md b/articles/bot-service-channel-connect-directline.md index d3aa58f63..43cac8188 100644 --- a/articles/bot-service-channel-connect-directline.md +++ b/articles/bot-service-channel-connect-directline.md @@ -1,54 +1,140 @@ --- -title: Connect a bot to Direct Line - Bot Service -description: Learn how to configure a bot's connection to Direct Line. +title: Connect a bot to Direct Line in Bot Framework SDK +description: Learn how to configure bots to communicate with client applications using the Direct Line channel. keywords: direct line, bot channels, custom client, connect to channels, configure -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 08/7/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to Direct Line -You can enable your own client application to communicate with your bot by using the Direct Line channel. +This article describes how to connect a bot to the **Direct Line** channel. Use this channel to communicate with a bot via your client application. -## Add the Direct Line channel +> [!NOTE] +> Direct Line is a standard channel over HTTPS protocol to allow communication between a client application and a bot. If you require network isolation instead, use the [Direct Line App Service Extension](bot-service-channel-directline-extension.md) over [WebSockets](https://tools.ietf.org/html/rfc6455). + +## Prerequisites + +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure. -To add the Direct Line channel, open the bot in the [Azure Portal](https://portal.azure.com/), click the **Channels** blade, and then click **Direct Line**. +## Add the Direct Line channel -![Add Direct Line channel](media/bot-service-channel-connect-directline/directline-addchannel.png) +The first thing you need to do is to add the Direct Line channel to your bot. -## Add new site +1. Go to the [Azure portal](https://portal.azure.com/). +1. Go to your Azure Bot resource. Under **Bot Settings**, select **Channels**. +1. Select **Direct Line** from the list of **Available Channels**. -Next, add a new site that represents the client application that you want to connect to your bot. Click **Add new site**, enter a name for your site, and click **Done**. +Your bot's now configured to use Direct Line using the **Default site**. -![Add Direct Line site](media/bot-service-channel-connect-directline/directline-addsite.png) +Alternatively, you can add a new site instead of using the default site. Select the **New site** button on the **Direct Line** channel page to create a new site. + :::image type="content" source="media/bot-service-channel-connect-directline/directline-newsite.png" alt-text="Direct Line new site button in Azure portal"::: ## Manage secret keys -When your site is created, the Bot Framework generates secret keys that your client application can use to [authenticate](~/rest-api/bot-framework-rest-direct-line-3-0-authentication.md) the Direct Line API requests that it issues to communicate with your bot. To view a key in plain text, click **Show** for the corresponding key. +When you add the Direct Channel, the Bot Framework generates secret keys. Your client application uses these keys to authenticate the Direct Line API requests that it issues to communicate with a bot. For more information, see [Authentication](rest-api/bot-framework-rest-direct-line-3-0-authentication.md). + +1. To view a site's Direct Line secret in plain text, go to the **Direct Line** channel page. +1. Select the **Direct Line** tab, then the site you want to get the key for, such as **Default_Site**. Azure will open a **Configure the site** pane. +1. Under **Secret keys**, select the eye icon next to the corresponding key. + + :::image type="content" source="media/bot-service-channel-connect-directline/directline-showkey.png" alt-text="Show Direct Line keys"::: -![Show Direct Line key](media/bot-service-channel-connect-directline/directline-showkey.png) +1. Copy and securely store the key. Use the key to [authenticate](rest-api/bot-framework-rest-direct-line-3-0-authentication.md) the Direct Line API requests that your client application issues to communicate with a bot. -Copy and securely store the key that is shown. Then use the key to [authenticate](~/rest-api/bot-framework-rest-direct-line-3-0-authentication.md) the Direct Line API requests that your client issues to communicate with your bot. -Alternatively, use the Direct Line API to [exchange the key for a token](~/rest-api/bot-framework-rest-direct-line-3-0-authentication.md#generate-token) that your client can use to authenticate its subsequent requests within the scope of a single conversation. + > [!NOTE] + > Secrets shouldn't be exposed or embedded in client applications. See next step. -![Copy Direct Line key](media/bot-service-channel-connect-directline/directline-copykey.png) +1. The best practice is to use the Direct Line API to [exchange the key for a token](rest-api/bot-framework-rest-direct-line-3-0-authentication.md#generate-token). The client application then will use the token to authenticate its requests within the scope of a single conversation. ## Configure settings -Finally, configure settings for the site. +To configure your site settings: + +1. On the Direct Line channel page, select the site you want to configure from the **Sites** list. The **Configure the site** pane will open, shown below: + :::image type="content" source="media/bot-service-channel-connect-directline/directline-configure-site.png" alt-text="Configure site pane"::: +1. Select the Direct Line protocol version that your client application will use to communicate with a bot. + + > [!TIP] + > If you're creating a new connection between your client application and bot, use Direct Line API 3.0. + +1. When finished, select **Apply** to save the site configuration. Repeat this process, beginning with a new site, for each client application that you want to connect to your bot. + +### Configure enhanced authentication + +One of the available site configurations is **Enhanced authentication options**, which helps to mitigate security risks when connecting to a bot (using the Web Chat control, for example). For more information, see [Direct Line enhanced authentication](v4sdk/bot-builder-security-enhanced.md). + +To add enhanced authentication: + +1. Enable **Enhance authentication options**. A message that says "You must have at least one trusted origin." will appear with an **Add a trusted origin** link. If you enable the enhanced authentication, you must specify at least one trusted origin. + + A trusted origin is a domain used by the system to authenticate users. In this case, Direct Line uses the domain to generate a token. + + - If you configure trusted origins as part of the configuration UI page, these settings will always be used as the only set for the generation of a token. Sending additional trusted origins (or setting trusted origins to none) when either generating a token or starting a conversation will be ignored (they aren't appended to the list or cross validated). + - If you didn't enable enhanced authentication, any origin URL you send as part of the API calls will be used. + :::image type="content" source="media/bot-service-channel-connect-directline/directline-enhanced-authentication.png" alt-text="Add trusted origin"::: +1. After adding a trusted domain URL, select **Apply**. + +## Direct Line example bot + +You can download a .NET example from this location: [Direct Line Bot Sample](https://github.com/microsoft/BotFramework-DirectLine-DotNet/tree/master/samples/core-DirectLine). + +The example contains two projects: + +- [DirectLineBot](https://github.com/microsoft/BotFramework-DirectLine-DotNet/tree/master/samples/core-DirectLine/DirectLineBot). It creates a bot to connect via a Direct Line channel. +- [DirectLineClient](https://github.com/microsoft/BotFramework-DirectLine-DotNet/tree/master/samples/core-DirectLine/DirectLineClient). This is a console application that talks to the previous bot via Direct Line channel. + +### Direct Line API + +- Credentials for the Direct Line API must be obtained from the Azure Bot registration, and will only allow the caller to connect to the bot for which they were generated. In the bot project, update the `appsettings.json` file with these values. + + ```csharp + { + "MicrosoftAppId": "", + "MicrosoftAppPassword": "" + } + ``` + +- In the Azure portal, enable Direct Line in the channels list, and then configure the Direct Line secret. Make sure that the checkbox for version 3.0 is checked. In the console client project, update the `App.config` file with the Direct Line secret key and the bot handle (Bot ID). -- Select the Direct Line protocol version that your client application will use to communicate with your bot. + ```xml + + + + + ``` -> [!TIP] -> If you are creating a new connection between your client application and bot, use Direct Line API 3.0. +User messages are sent to the bot using the Direct Line Client `Conversations.PostActivityAsync` method using the `ConversationId` generated previously. -When finished, click **Done** to save the site configuration. You can repeat this process, beginning with [Add new site](#add-new-site), for each client application that you want to connect to your bot. +```csharp +while (true) +{ + string input = Console.ReadLine().Trim(); -When you have the **enhanced authentication enabled**, you will see the following behavior for which trusted origins are used: + if (input.ToLower() == "exit") + { + break; + } + else + { + if (input.Length > 0) + { + Activity userMessage = new Activity + { + From = new ChannelAccount(fromUser), + Text = input, + Type = ActivityTypes.Message + }; -- If you configure trusted origins as part of the configuration UI page, then these will **always** be used as the only set. Sending no or additional trusted origins when generating a token or starting a conversation will be ignored (i.e. they are **not appended** to the list or cross validated). -- If you have not configured trusted origins as part of the configuration UI, then any value you send as part of the API calls will be used. \ No newline at end of file + await client.Conversations.PostActivityAsync(conversation.ConversationId, userMessage); + } + } +} +``` diff --git a/articles/bot-service-channel-connect-directlinespeech.md b/articles/bot-service-channel-connect-directlinespeech.md deleted file mode 100644 index 14f03c659..000000000 --- a/articles/bot-service-channel-connect-directlinespeech.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Connect a bot to Direct Line Speech -titleSuffix: Bot Service -description: Overview and steps required to connect an existing Bot Framework bot to the Direct Line Speech channel for voice in, voice out interaction with high reliability and low latency. -services: bot-service -author: trrwilson -manager: nitinme -ms.service: bot-service -ms.topic: conceptual -ms.date: 11/02/2019 -ms.author: travisw ---- - -# Connect a bot to Direct Line Speech - -[!INCLUDE[applies-to-v4](includes/applies-to.md)] - -You can configure your bot to allow client applications to communicate with it through the Direct Line Speech channel. - -Once you have built your bot, onboarding it with Direct Line Speech will enable low latency, high reliability connection with client applications using the [Speech SDK](https://aka.ms/speech/sdk). These connections are optimized for voice in, voice out conversational experiences. For more information on Direct Line Speech and how to build client applications, visit the [custom voice-first virtual assistant](https://aka.ms/bots/speech/va) page. - -## Add the Direct Line Speech channel - -1. To add the Direct Line Speech Channel, first open the bot in the [Azure Portal](https://portal.azure.com), From your resources, select your **Bot Channel Registration** resource. Click on **Channels** in the configuration blade. - - ![highlight of the location for selecting channels to connect to](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-selectchannel.png "selecting channels") - -1. In the channel selection page, find and click `Direct Line Speech` to choose the channel. - - ![selecting direct line speech channel](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-connectspeechchannel.png "connecting Direct Line Speech") - -1. Direct Line Speech Channel requires a Cognitive Services resource. You can either use an existing resource or create a new Cognitive Services resource following the [instructions](https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account). - - ![selecting direct line speech channel](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-cognitivesericesaccount-selection.png "selecting Cogntive Services resource") - -1. Once you've reviewed the terms of use, click `Save` to confirm your channel selection. - - ![saving the enablement of Direct Line Speech channel](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-savechannel.png "Save the channel configuration") - -## Enable the Bot Framework Protocol Streaming Extensions - -With the Direct Line Speech channel connected to your bot, you now need to enable Bot Framework Protocol Streaming Extensions support for optimal, low-latency interaction. - -1. If you haven't already, open the blade for your bot in the [Azure Portal](https://portal.azure.com). - -1. Click on **Settings** under the **Bot Management** category (right below **Channels**). Click the checkbox for **Enable Streaming Endpoint**. - - ![enable the streaming protocol](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablestreamingsupport.png "enable streaming extension support") - -1. At the top of the page, click **Save**. - -1. On the same blade, under the **App Service Settings** category, click **Configuration**. - - ![navigate to app service settings](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-configureappservice.png "configure the app service") - -1. Click on `General settings` and then select the option to enable `Web socket` support. - - ![enable websockets for the app service](media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablewebsockets.png "enable websockets") - -1. Click `Save` at the top of the configuration page. - -1. The Bot Framework Protocol Streaming Extensions are now enabled for your bot. You are now ready to update your bot code and [integrate Streaming Extensions support](https://aka.ms/botframework/addstreamingprotocolsupport) to an existing bot project. - -## Adding protocol support to your bot - -With the Direct Line Speech channel connected and support for the Bot Framework Protocol Streaming Extensions enabled, all that's left is to add code to your bot to support the optimized communication. Follow the instructions on [adding Streaming Extensions support to your bot](https://aka.ms/botframework/addstreamingprotocolsupport) to ensure full compatibility with Direct Line Speech. - - diff --git a/articles/bot-service-channel-connect-email.md b/articles/bot-service-channel-connect-email.md index bb2ad2b76..3556d411a 100644 --- a/articles/bot-service-channel-connect-email.md +++ b/articles/bot-service-channel-connect-email.md @@ -1,91 +1,98 @@ --- -title: Connect a bot to Office 365 email - Bot Service -description: Learn how to configure a bot to send and receive email with Office 365. +title: Connect a bot to email - Bot Service +description: Learn how to configure bots to send and receive email messages by connecting them to Microsoft 365 email. See how to customize messages. keywords: Office 365, bot channels, email, email credentials, azure portal, custom email -author: kamrani -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/15/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Connect a bot to Office 365 email -Bots can communicate with users via Office 365 email in addition to other [channels](~/bot-service-manage-channels.md). When a bot is configured to access an email account, it receives a message when a new email arrives. The bot can then respond as indicated by its business logic. For example, the bot could send an email reply acknowledging an email was received with the message, "Hi! Thanks for your order! We will begin processing it immediately." +# Connect a bot to email + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +You can configure your bot to communicate with users via Microsoft 365 email. When you configure your bot to access an email account, it receives a message when a new email arrives. The bot can then use email to respond. For example, in response to a user's email message, the bot could send an email reply with the message, "Hi! Thanks for your order! We will begin processing it immediately." + +The Email channel currently works with Office 365 only. Other email services aren't currently supported. > [!WARNING] -> It is a violation of the Bot Framework [Code of Conduct](https://www.botframework.com/Content/Microsoft-Bot-Framework-Preview-Online-Services-Agreement.htm) to create "spambots", including bots that send unwanted or unsolicited bulk email. +> +> - For security reasons, Exchange Online will disable _basic authentication_ on October 1st, 2022. The Email channel now supports the new Exchange Online _modern authentication_ model. Bots that use the basic authentication model will experience failures after the October 2022 change; or earlier if your tenant administrator disables basic authentication before that date. For more information, see [Basic Authentication and Exchange Online - September 2021 Update](https://techcommunity.microsoft.com/t5/exchange-team-blog/basic-authentication-and-exchange-online-september-2021-update/ba-p/2772210). +> - It's a violation of the Bot Framework [Code of Conduct](https://www.botframework.com/Content/Developer-Code-of-Conduct-for-Microsoft-Bot-Framework.htm) to create "spambots", including bots that send unwanted or unsolicited bulk email. + +## Prerequisites + +- If you don't have an Azure subscription, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A dedicated Office 365 email account for the bot. +- Permission to grant the bot `Mail.ReadWrite` and `Mail.Send` access. For more information, see [Understanding Microsoft Entra ID application consent experiences](/azure/active-directory/develop/application-consent-experience). > [!NOTE] -> If you are using Microsoft Exchange Server, make sure you have enabled [Autodiscover](https://docs.microsoft.com/exchange/client-developer/exchange-web-services/autodiscover-for-exchange) first before configuring email channel. +> You shouldn't use your own personal email accounts for bots, as every message sent to that email account will be forwarded to the bot. This can result in the bot inappropriately sending a response to a sender. For this reason, bots should only use dedicated M365 email accounts. -## Configure email credentials +## Configure email to use modern authentication -You can connect a bot to the Email channel by entering Office 365 credentials in the Email channel configuration. -Federated authentication using any vendor that replaces AAD is not supported. +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. In the left pane, select **Channels**. +1. Select **Email** to open the **Configure Email** blade. -> [!NOTE] -> You should not use your own personal email accounts for bots, as every message sent to that email account will be forwarded to the bot. This can result in the bot inappropriately sending a response to a sender. For this reason, bots should only use dedicated O365 email accounts. + :::image type="content" source="media/bot-service-channel-connect-email/bot-service-channel-connect-modern-authentication.png" alt-text="Configure email settings"::: -To add the Email channel, open the bot in the [Azure Portal](https://portal.azure.com/), click the **Channels** blade, and then click **Email**. Enter your valid email credentials and click **Save**. + 1. Set **Authentication type** to **Modern authentication (OAUTH)**. + 1. In **Email Address**, enter the dedicated Office 365 email account for the bot. + 1. Select **Authorize**. + 1. When prompted, sign in to the email account and grant read/write and send permissions to the bot. + 1. On success, a page opens with a validation code. Copy the validation code. -![Enter email credentials](~/media/bot-service-channel-connect-email/bot-service-channel-connect-email-credentials.png) + :::image type="content" source="media/bot-service-channel-connect-email/bot-service-channel-validation-code.png" alt-text="Interaction with validation code"::: -The Email channel currently works with Office 365 only. Other email services are not currently supported. + 1. In **Authentication code**, enter the validation code you just copied. + 1. Select **Apply** to complete email configuration. -## Customize emails +## Configure email to use basic authentication -The Email channel supports sending custom properties to create more advanced, customized emails using the `channelData` property. +> [!NOTE] +> +> - Federated authentication using any vendor that replaces Microsoft Entra ID isn't supported. +> - For security reasons, usage of _basic authentication_ in Exchange Online is being disabled on October 1st, 2022. +> You should migrate all of your bots to use _modern authentication_ before the deadline. +> - If you use Microsoft Exchange Server, make sure you've enabled [Autodiscover](/exchange/client-developer/exchange-web-services/autodiscover-for-exchange) first, before configuring email to use basic authentication. +> - If you're using an Office 365 account with MFA enabled on it, make sure you disable MFA for the specified account first; then you can configure the account for the email channel. Otherwise, the connection will fail. -[!INCLUDE [Email channelData table](~/includes/snippet-channelData-email.md)] +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. In the left pane, select **Channels (preview)** to open the **Channels** blade. +1. Select **Email** to open the **Configure Email** blade. -The following example message shows a JSON file that includes these `channelData` properties. + :::image type="content" source="media/bot-service-channel-connect-email/bot-service-channel-connect-basic-authentication.png" alt-text="Enter email credentials"::: -```json -{ - "type": "message", - "locale": "en-Us", - "channelID": "email", - "from": { "id": "mybot@mydomain.com", "name": "My bot"}, - "recipient": { "id": "joe@otherdomain.com", "name": "Joe Doe"}, - "conversation": { "id": "123123123123", "topic": "awesome chat" }, - "channelData": - { - "htmlBody": "This is more than awesome.", - "subject": "Super awesome message subject", - "importance": "high", - "ccRecipients": "Yasemin@adatum.com;Temel@adventure-works.com" - } -} -``` + 1. Set **Authentication type** to **Basic authentication (disabling staring October, 2022)**. + 1. In **Email Address**, enter the dedicated Office 365 email account for the bot. + 1. In **Password**, enter the password for the email account. + 1. Select **Apply** to complete email configuration. -::: moniker range="azure-bot-service-3.0" -For more information about using `channelData`, see the [Node.js](https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/core-ChannelData) sample or [.NET](~/dotnet/bot-builder-dotnet-channeldata.md) documentation. -::: moniker-end +## Customize emails -::: moniker range="azure-bot-service-4.0" -For more information about using `channelData`, -see [how to implement channel-specific functionality](~/v4sdk/bot-builder-channeldata.md). -::: moniker-end +The Email channel supports sending custom values to create more advanced, customized emails by using the activity `channelData` property. +The snippet below shows an example of the `channelData` for an incoming custom email message, from the bot to the user. -## Other considerations +[!INCLUDE [email channelData json](includes/snippet-channelData-email.md)] -If your bot does not return a 200 OK HTTP status code within 15 seconds in response to an incoming email message, the email channel will try to resend the message, and your bot may receive the same email message activity a few times. For more information, see the [HTTP details](v4sdk/bot-builder-basics.md#http-details) section in **How bots work** and the how to [troubleshooting timeout errors](https://github.com/daveta/analytics/blob/master/troubleshooting_timeout.md) article. +For more information about the activity `channelData` property, see [Create a custom Email message](v4sdk/bot-builder-channeldata.md#create-a-custom-email-message). -> [!NOTE] -> If you are using an Office 365 account with MFA enabled on it, make sure you disable MFA for the specified account first, then you can configure the account for the email channel. Otherwise, the connection will fail. +## Troubleshoot -## Additional resources +For errors that can occur while processing consent to an application, see [Understanding Microsoft Entra ID application consent experiences](/azure/active-directory/develop/application-consent-experience) and [Unexpected error when performing consent to an application](/azure/active-directory/manage-apps/application-sign-in-unexpected-user-consent-error). + +If your bot doesn't return a 200 OK HTTP status code within 15 seconds in response to an incoming email message, the email channel will try to resend the message, and your bot may receive the same email message activity a few times. For more information, see the [HTTP details](v4sdk/bot-builder-basics.md#http-details) section in **How bots work** and the [troubleshooting timeout errors](https://github.com/daveta/analytics/blob/master/troubleshooting_timeout.md) article. - -::: moniker range="azure-bot-service-3.0" -* Connect a bot to [channels](~/bot-service-manage-channels.md) -* [Implement channel-specific functionality](dotnet/bot-builder-dotnet-channeldata.md) with the Bot Framework SDK for .NET -* Read the [channels reference](bot-service-channels-reference.md) article for more information about which features are supported on each channel -::: moniker-end -::: moniker range="azure-bot-service-4.0" -* Connect a bot to [channels](~/bot-service-manage-channels.md) -* [Implement channel-specific functionality](~/v4sdk/bot-builder-channeldata.md) with the Bot Framework SDK for .NET -* Read the [channels reference](bot-service-channels-reference.md) article for more information about which features are supported on each channel -::: moniker-end +## Additional resources +- Connect a bot to [channels](bot-service-manage-channels.md) +- [Implement channel-specific functionality](v4sdk/bot-builder-channeldata.md) with the Bot Framework SDK for .NET +- Read the [channels reference](bot-service-channels-reference.md) article for more information about which features are supported on each channel diff --git a/articles/bot-service-channel-connect-facebook.md b/articles/bot-service-channel-connect-facebook.md index 191e2cece..87afc226b 100644 --- a/articles/bot-service-channel-connect-facebook.md +++ b/articles/bot-service-channel-connect-facebook.md @@ -1,402 +1,177 @@ --- -title: Connect a bot to Facebook Messenger - Bot Service -description: Learn how to configure a bot's connection to Facebook Messenger. -keywords: Facebook Messenger, bot channel, Facebook App, App ID, App Secret, Facebook bot, credentials -manager: kamrani -ms.topic: article -ms.author: kamrani -ms.service: bot-service -ms.date: 01/16/2020 +title: Connect a Bot Framework bot to Facebook +description: Learn how to configure bots to connect to Facebook Messenger and Facebook Workplace and communicate with users via Facebook. +keywords: Facebook Messenger, bot channel, Facebook App, Facebook bot +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to Facebook -Your bot can be connected to both Facebook Messenger and Facebook Workplace, so that it can communicate with users on both platforms. The following tutorial shows how to connect a bot to these two channels. +You can configure your bot to communicate with people through Facebook Messenger or Facebook Workplace. This article describes how to create a Facebook app using the Meta for Developers site, connect your bot to your Facebook app in Azure, and test your bot on Facebook. -> [!NOTE] -> The Facebook UI may appear slightly different depending on which version you are using. - -## Connect a bot to Facebook Messenger - -To learn more about developing for Facebook Messenger, see the [Messenger platform documentation](https://developers.facebook.com/docs/messenger-platform). You may wish to review Facebook's [pre-launch guidelines](https://developers.facebook.com/docs/messenger-platform/product-overview/launch#app_public), [quick start](https://developers.facebook.com/docs/messenger-platform/guides/quick-start), and [setup guide](https://developers.facebook.com/docs/messenger-platform/guides/setup). - -To configure a bot to communicate using Facebook Messenger, enable Facebook Messenger on a Facebook page and then connect the bot. - -### Copy the Page ID - -The bot is accessed through a Facebook Page. - -1. [Create a new Facebook Page](https://www.facebook.com/bookmarks/pages) or go to an existing Page. - -1. Open the Facebook Page's **About** page and then copy and save the **Page ID**. +This article shows how to add the Facebook channel to your bot via Azure portal. For information on how to use a custom channel adapter, see [Additional information](#additional-information). -### Create a Facebook app +## Prerequisites -1. In your browser, navigate to [Create a new Facebook App](https://developers.facebook.com/quickstarts/?platform=web). -1. Enter the name of your app and click the **Create New Facebook App ID** button. +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A bot published to Azure that you want to connect to Facebook. +- A Facebook for Developers account. If you don't have an account, you can create one at [developers.facebook.com](https://developers.facebook.com). +- A Facebook page from which users will access your bot. If you don't have one yet, [Create a new Page](https://www.facebook.com/help/104002523024878). +- To use Facebook Workplace with your bot, you must create a Workplace account and a custom integration to connect the bot to. - ![Create App](media/channels/fb-create-messenger-bot-app.png) +## Create a Facebook app -1. In the displayed dialog, enter your email address and click the **Create App ID** button. +### [Facebook Messenger](#tab/messenger) - ![Create an App ID](media/channels/fb-create-messenger-bot-app-id.png) +Users will access your bot from a Facebook Page. To connect the bot, you'll enable Facebook Messenger on the Facebook Page and then connect the bot to the Page. -1. Go through the wizard steps. +### Create your app -1. Enter the required check information, then click the **Skip Quick Start** button in the upper right. +1. Sign in to your [Meta for Developers](https://developers.facebook.com) account. +1. Go to [Create a new Facebook App](https://developers.facebook.com/apps/create). +1. On the **Select an app type** page, select **Business** and then **Next**. +1. On the **Provide basic information** page, enter a name for your app and select **Create app**. + - If prompted, enter your password and select **Submit** to create your app. + - After your app is created, the site goes to a page for your app. +1. Expand **Settings** and select **Basic**. + 1. Copy and save the **App ID** and **App Secret**. +1. Now under **Settings**, select **Advanced**. + 1. In the resulting pane, scroll down to the **Security** settings, and enable **Allow API Access to app settings**. + 1. Select **Save Changes**. -1. In the left pane of the next displayed window, expand *Settings* and click **Basic**. +### Enable Messenger -1. In the right pane, copy and save the **App ID** and **App Secret**. +1. Select **Dashboard**. +1. In the resulting pane, scroll down to the **Messenger** tile and select **Set Up**. +1. The site adds Messenger settings to your app and displays the settings page. - ![Copy App ID and App Secret](media/channels/fb-messenger-bot-get-appid-secret.png) +### Add pages and generate tokens -1. In the left pane, under *Settings*, click **Advanced**. +1. Under **Messenger**, select **Settings**. +1. Scroll down to **Access Tokens** and select **Add or Remove Pages**. + 1. When prompted for the identity to associate with Messenger, either continue with your current account or sign in to another. + 1. When prompted for the Pages you want to use with your app, select the pages and then select **Next**. + 1. If prompted to submit the request for Login Review, review the information and select **Done**. + 1. On success, the site displays a success page. Select **OK** to continue. -1. In the right pane, set **Allow API Access to App Settings** slider to **Yes**. +1. The Page you added now appears in the **Pages** list. - ![Copy App ID and App Secret](media/channels/fb-messenger-bot-api-settings.png) + Copy and save the Page ID for later. -1. In the page bottom right, click the **Save Changes** button. +1. Select **Generate token** for the Page. + 1. The site displays security information and gives you a chance to copy the token. + 1. Read and acknowledge the warning. + 1. Copy the token and select **Done**. -### Enable messenger +You now have your app information and a token for the next step. +Leave the Facebook setting steps pending. You'll update them later. -1. In the left pane, click **Dashboard**. -1. In the right pane, scroll down and in the **Messenger** box, click the **Set Up** button. The Messenger entry is displayed under the *PRODUCTS* section in the left pane. +### Configure your bot in Azure - ![Enable messenger](media/channels/fb-messenger-bot-enable-messenger.png) +To let your bot send messages and other events to Facebook Messenger, enable webhooks integration. -### Generate a Page Access Token +1. In a new browser window, go to the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **Facebook**. +1. In **Configure Facebook Channel**, enter the Facebook information you copied in the previous steps. + 1. Enter your **Facebook App ID** and **Facebook App Secret**. + 1. Enter your **Page ID** and **Access Token**. + 1. Copy the generated _callback URL_ and _verify token_ values. + 1. Select **Add**. -1. In the left pane, under the Messenger entry, click **Settings**. -1. In the right pane, scroll down and in the **Token Generation** section, select the target page. - - ![Enable messenger](media/channels/fb-messenger-bot-select-messenger-page.png) - -1. Click the **Edit Permissions** button to grant the app pages_messaging in order to generate an access token. -1. Follow the wizard steps. In the last step accept the default settings and click the **Done** button. At the end a **page access token** is generated. - - ![Messenger permissions](media/channels/fb-messenger-bot-permissions.png) - -1. Copy and save the **Page Access Token**. +> [!TIP] +> +> - If you don't have a copy of your information from the previous steps, you can retrieve it for the Meta for Developers site. +> - If you need to, create a new web token for the page. For instructions, see [Add pages and generate tokens](#add-pages-and-generate-tokens). ### Enable webhooks -In order to send messages and other events from your bot to Facebook Messenger, you must enable webhooks integration. At this point, let's leave the Facebook setting steps pending; will come back to them. - -1. In your browser open a new window and navigate to the [Azure portal](https://portal.azure.com/). - -1. In the Resource list, click on the bot resource registration and in the related blade click **Channels**. - -1. In the right pane, click the **Facebook** icon. - -1. In the wizard enter the Facebook information stored in the previous steps. If the information is correct, at the bottom of the wizard, you should see the **callback URL** and the **verify token**. Copy and store them. - - ![fb messenger channel config](media/channels/fb-messenger-bot-config-channel.PNG) - -1. Click the **Save** button. - -1. Let's go back to the Facebook settings. In the right pane, scroll down and in the **Webhooks** section, click the **Subscribe To Events** button. This is to forward messaging events from Facebook Messenger to the bot. - - ![Enable webhooks](media/channels/fb-messenger-bot-webhooks.PNG) - -1. In the displayed dialog, enter the **Callback URL** and **Verify Token** values stored previously. Under **Subscription Fields**, select *message\_deliveries*, *messages*, *messaging\_optins*, and *messaging\_postbacks*. - - ![Config webhooks](media/channels/fb-messenger-bot-config-webhooks.png) - -1. Click the **Verify and Save** button. - -1. Select the Facebook page to subscribe the webhook. Click the **Subscribe** button. - - ![Config webhooks page](media/channels/fb-messenger-bot-config-webhooks-page.PNG) - -### Submit for review - -Facebook requires a Privacy Policy URL and Terms of Service URL on its basic app settings page. The [Code of Conduct](https://investor.fb.com/corporate-governance/code-of-conduct/default.aspx) page contains third party resource links to help create a privacy policy. The [Terms of Use](https://www.facebook.com/terms.php) page contains sample terms to help create an appropriate Terms of Service document. - -After the bot is finished, Facebook has its own [review process](https://developers.facebook.com/docs/messenger-platform/app-review) for apps that are published to Messenger. The bot will be tested to ensure it is compliant with Facebook's [Platform Policies](https://developers.facebook.com/docs/messenger-platform/policy-overview). - -### Make the App public and publish the Page - -> [!NOTE] -> Until an app is published, it is in [Development Mode](https://developers.facebook.com/docs/apps/managing-development-cycle). Plugin and API functionality will only work for admins, developers, and testers. - -After the review is successful, in the App Dashboard under App Review, set the app to Public. -Ensure that the Facebook Page associated with this bot is published. Status appears in Pages settings. - -## Connect a bot to Facebook Workplace +Go back to the Facebook settings to finish up the configuration process. + +1. On the Meta for Developers site, go back to the Messenger settings page for your app. +1. In the resulting pane, scroll down to the **Webhooks** section and select **Add Callback URL**. +1. On the **Edit Callback URL** page: + 1. Enter the callback URL and verify token values that you copied from the Azure portal. + 1. Select **Verify and save**. +1. The Page you added now appears in the **Pages** list under **Webhooks**. +1. Select **Add subscriptions** for the Page. + 1. On the **Edit page subscriptions** page, select the following subscription fields: + - **messages** + - **messaging_postbacks** + - **messaging_options** + - **message_deliveries** + 1. Select **Save**. +1. The site displays the added subscription fields next to the Page for your bot. + +### [Facebook Workplace](#tab/workplace) > [!NOTE] -> On December 16, 2019, Workplace by Facebook changed its security model for custom integrations. Prior integrations built using Microsoft Bot Framework v4 need to be updated to use the Bot Framework Facebook adapters per the instructions below prior to February 28, 2020. -> -> Facebook will only consider integrations with limited access to Workplace data (low sensitivity permissions) eligible for continued use until December 31, 2020 if such integrations have completed and passed Security RFI and if the developer reaches out before January 15, 2020 via [Direct Support](https://my.workplace.com/work/admin/direct_support) to request continued use of the app. -> -> Bot Framework adapters are available for [JavaScript/Node.js](https://aka.ms/botframework-workplace-adapter) and [C#/.NET](https://aka.ms/bf-workplace-csharp) bots. - -Facebook Workplace is a business-oriented version of Facebook, which allows employees to easily connect and collaborate. It contains live videos, news feeds, groups, messenger, reactions, search, and trending posts. It also supports: - -- Analytics and integrations. A dashboard with analytics, integration, single sign-on, and identity providers that companies use to integrate Workplace with their existing IT systems. -- Multi-company groups. Shared spaces in which employees from different organizations can work together and collaborate. - -See the [Workplace Help Center](https://workplace.facebook.com/help/work/) to learn more about Facebook Workplace and [Workplace Developer Documentation](https://developers.facebook.com/docs/workplace) for guidelines about developing for Facebook Workplace. - -To use Facebook Workplace with your bot, you must create a Workplace account and a custom integration to connect the bot. - -### Create a Workplace Premium account - -1. Submit an application to [workplace](https://www.facebook.com/workplace) on behalf of your company. -1. Once your application has been approved, you will receive an email inviting you to join. The response may take a while. -1. From the e-mail invitation, click **Get Started**. -1. Enter your profile information. - > [!TIP] - > Set yourself as the system administrator. Remember that only system administrators can create custom integrations. -1. Click **Preview Profile** and verify the information is correct. -1. Access *Free Trial*. -1. Create **password**. -1. Click **Invite Coworkers** to invite employees to sign-in. The employees you invited will become members as soon as they sign. They will go through a similar sign-in process as described in these steps. +> On December 16, 2019, Workplace by Facebook changed its security model for custom integrations. Facebook is not accepting new app integrations for Facebook Workplace. -### Create a custom integration - -Create a [custom integration](https://developers.facebook.com/docs/workplace/custom-integrations-new) for your Workplace following the steps described below. When you create a custom integration, an app with defined permissions and a page of type 'Bot' only visible within your Workplace community are created. - -1. In the **Admin Panel**, open the **Integrations** tab. -1. Click on the **Create your own custom App** button. - - ![Workplace Integration](media/channels/fb-integration.png) - -1. Choose a display name and a profile picture for the app. Such information will be shared with the page of type 'Bot'. -1. Set the **Allow API Access to App Settings** to "Yes". -1. Copy and safely store the App ID, App Secret and App Token that's shown to you. - - ![Workplace Keys](media/channels/fb-keys.png) - -1. Now you have finished creating a custom integration. You can find the page of type 'Bot' in your Workplace community,as shown below. - - ![Workplace page](media/channels/fb-page.png) +--- -### Update your bot code with Facebook adapter +## Make your app public -Your bot's source code needs to be updated to include an adapter to communicate with Workplace by Facebook. Adapters are available for [JavaScript/Node.js](https://aka.ms/botframework-workplace-adapter) and [C#/.NET](https://aka.ms/bf-workplace-csharp) bots. +Until an app is published, it's in [Development Mode](https://developers.facebook.com/docs/apps/managing-development-cycle). Plugin and API functionality will only work for admins, developers, and testers. -### Provide Facebook credentials +Only the creator (the Facebook Dev account that created the page and bot) can get a bot response. Normal Facebook users can't see the page or the bot. Give dev or test roles to target users, so they can also chat with bot. -You will need to update appsettings.json of your bot with **Facebook App ID**, **Facebook App Secret** and **Page Access Token** values copied from the Facebook Workplace previously. Instead of a traditional pageID, use the numbers following the integrations name on its **About** page. Follow these instructions to update your bot source code in [JavaScript/Node.js](https://aka.ms/botframework-workplace-adapter) or [C#/.NET](https://aka.ms/bf-workplace-csharp). +Users to be added to tester roles must first register on the Meta for Developers site. The tester role is not available to Facebook users that don't have a Meta for Developers account. For more information about app roles and test users, see the [Meta for Developers developer documentation](https://developers.facebook.com/docs/). ### Submit for review -Please refer to the **Connect a bot to Facebook Messenger** section and [Workplace Developer Documentation](https://developers.facebook.com/docs/workplace) for details. - -### Make the App public and publish the Page - -Please refer to the **Connect a bot to Facebook Messenger** section for details. - -### Setting the API version - -If you receive a notification from Facebook about deprecation of a certain version of the Graph API, go to [Facebook developers page](https://developers.facebook.com). Navigate to your bot’s **App Settings** and go to **Settings > Advanced > Upgrade API version**, then switch **Upgrade All Calls** to 3.0. - -![API version upgrade](media/channels/fb-version-upgrade.png) - -## Connect a bot to Facebook using the Facebook adapter - -Use the Bot Framework Facebook adapter to connect your bot with Facebook Workplace. To connect to Facebook messenger, you can use the Facebook channel or the Facebook adapter. -Facebook adapters are available for [JavaScript/Node.js](https://aka.ms/botframework-workplace-adapter) and [C#/.NET](https://aka.ms/bf-workplace-csharp) bots. - -In this article you will learn how to connect a bot to Facebook using the adapter. This article will walk you through modifying the EchoBot sample to connect it to Facebook. - -The instructions below cover the C# implementation of the Facebook adapter. For instructions on using the JavaScript adapter, part of the BotKit libraries, [see the BotKit Facebook documentation](https://botkit.ai/docs/v4/platforms/facebook.html). - -### Prerequisites - -- The [EchoBot sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/02.echo-bot) -- A Facebook for Developers account. If you do not have an account, you can [create one here](https://developers.facebook.com). - -### Create a Facebook app, page and gather credentials - -1. Log into [https://developers.facebook.com](https://developers.facebook.com). Click **My Apps** in the main menu and click **Create App** from the drop down menu. - -![Create app](media/bot-service-channel-connect-facebook/create-app-button.png) - -1. In the pop-up window that appears, enter a name for your new app and then click **Create App ID**. - -![Define app name](media/bot-service-channel-connect-facebook/app-name.png) - -#### Set up Messenger and associate a Facebook page - -1. Once your app has been created, you will see a list of products available to set up. Click the **Set Up** button next to the **Messenger** product. - -1. You now need to associate your new app with a Facebook page (if you do not have an existing page you want to use, you can create one by clicking **Create New Page** in the **Access Tokens** section). Click **Add or Remove Pages**, select the page you want to associated with your app and click **Next**. Leave the **Manage and access Page conversations on Messenger** setting enabled and click **Done**. - -![Set up messenger](media/bot-service-channel-connect-facebook/app-page-permissions.png) - -1. Once you have associated your page, click the **Generate Token** button to generate a page access token. Make a note of this token as you will need it in a later step when configuring your bot application. - -#### Obtain your app secret - -1. In the left hand menu, click **Settings** and then click **Basic** to navigate to the basic setting page for your app. - -1. On the basic settings page, click the **Show** button next to your **App Secret**. Make a note of this secret as you will need it in a later step when configuring your bot application. - -### Wiring up the Facebook adapter in your bot - -Now that you have your Facebook app, page and credentials, you need to configure your bot application. - -#### Install the Facebook adapter NuGet package - -Add the [Microsoft.Bot.Builder.Adapters.Facebook](https://www.nuget.org/packages/Microsoft.Bot.Builder.Adapters.Facebook/) NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs). - -#### Create a Facebook adapter class - -Create a new class that inherits from the ***FacebookAdapter*** class. This class will act as our adapter for the Facebook channel and include error handling capabilities (similar to the ***BotFrameworkAdapterWithErrorHandler*** class already in the sample, used for handling other requests from Azure Bot Service). - -```csharp -public class FacebookAdapterWithErrorHandler : FacebookAdapter -{ - public FacebookAdapterWithErrorHandler(IConfiguration configuration, ILogger logger) - : base(configuration, logger) - { - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); - - // Send a message to the user - await turnContext.SendActivityAsync("The bot encountered an error or bug."); - await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); - - // Send a trace activity, which will be displayed in the Bot Framework Emulator - await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); - }; - } -} -``` - -#### Create a new controller for handling Facebook requests - -Create a new controller which will handle requests from Facebook, on a new endpoing 'api/facebook' instead of the default 'api/messages' used for requests from Azure Bot Service Channels. By adding an additional endpoint to your bot, you can accept requests from Bot Service channels, as well as from Facebook, using the same bot. - -```csharp -[Route("api/facebook")] -[ApiController] -public class FacebookController : ControllerBase -{ - private readonly FacebookAdapter _adapter; - private readonly IBot _bot; - - public FacebookController(FacebookAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } - - [HttpPost] - [HttpGet] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _adapter.ProcessAsync(Request, Response, _bot); - } -} -``` - -#### Inject the Facebook adapter in your bot startup.cs - -Add the following line to the ***ConfigureServices*** method within your startup.cs file. This will register your Facebook adapter and make it available for your new controller class. The configuration settings you added in the previous step will be automatically used by the adapter. - -```csharp -services.AddSingleton(); -``` - -Once added, your ***ConfigureServices*** method shold look like this. - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - // Create the default Bot Framework Adapter (used for Azure Bot Service channels and emulator). - services.AddSingleton(); - - // Create the Facebook Adapter - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -#### Obtain a URL for your bot - -Now that you have wired up the adapter in your bot project, you need to provide to Facebook the correct endpoint for your application, so that your bot will receive messages. You also need this URL to complete configuration of your bot application. - -To complete this step, [deploy your bot to Azure](https://aka.ms/bot-builder-deploy-az-cli) and make a note of the URL of your deployed bot. - -> [!NOTE] -> If you are not ready to deploy your bot to Azure, or wish to debug your bot when using the Facebook adapter, you can use a tool such as [ngrok](https://www.ngrok.com) (which you will likely already have installed if you have used the Bot Framework emulator previously) to tunnel through to your bot running locally and provide you with a publicly accessible URL for this. -> -> If you wish create an ngrok tunnel and obtain a URL to your bot, use the following command in a terminal window (this assumes your local bot is running on port 3978, alter the port numbers in the command if your bot is not). -> -> ```cmd -> ngrok.exe http 3978 -host-header="localhost:3978" -> ``` - -#### Add Facebook app settings to your bot's configuration file - -Add the settings shown below to your appSettings.json file in your bot project. You populate **FacebookAppSecret** and **FacebookAccessToken** using the values you gathered when creating and configuring your Facebook App. **FacebookVerifyToken** should be a random string that you create and will be used to ensure your bot's endpoint is authenitic when called by Facebook. - -```json - "FacebookVerifyToken": "", - "FacebookAppSecret": "", - "FacebookAccessToken": "" -``` - -Once you have populated the settings above, you should redeploy (or restart if running locally with ngrok) your bot. - -### Complete configuration of your Facebook app - -The final step is to configure your new Facebook app's Messenger endpoint, to ensure your bot receives messages. - -1. Within the dashboard for your app, click **Messenger** in the left hand menu and then click **Settings**. - -1. In the **Webhooks** section click **Add Callback URL**. - -1. In the **Callback URL** text box enter your bot's URL, plus the `api/facebook` endpoint you specified in your newly created controller. For example, `https://yourboturl.com/api/facebook`. In the **Verify Token** text box enter the verify token you created earlier and used in your bot application's appSettings.json file. - - ![Edit callback url](media/bot-service-channel-connect-facebook/edit-callback-url.png) +Facebook requires a Privacy Policy URL and Terms of Service URL on its basic app settings page. The [Code of Conduct](https://investor.fb.com/corporate-governance/code-of-conduct/default.aspx) page contains third party resource links to help create a privacy policy. The [Terms of Use](https://www.facebook.com/terms.php) page contains sample terms to help create an appropriate Terms of Service document. -1. Click **Verify and Save**. Ensure you bot is running, as Facebook will make a request to your application's endpoint and verify it using your **Verify Token**. +After the bot is finished, Facebook has its own [review process](https://developers.facebook.com/docs/messenger-platform/app-review) for apps that are published to Messenger. The bot will be tested to ensure it's compliant with Facebook's [Platform Policies](https://developers.facebook.com/docs/messenger-platform/policy-overview). -1. Once your callback URL has been verified, click the **Add Subscriptions** button that is now shown. In the pop-up window, select the following subscriptions and click **Save**. +### Make the app public and publish the Page - - **messages** - - **messaging_postbacks** - - **messaging_optins** - - **messaging_deliveries** +After the review is successful, in the App Dashboard under App Review, set the app to Public. Ensure that the Facebook Page associated with this bot is published. Status appears in Pages settings. - ![Webhook subscriptions](media/bot-service-channel-connect-facebook/webhook-subscriptions.png) +### Set the API version -### Test your bot with adapter in Facebook +If you receive a notification from Facebook about deprecation of a certain version of the Graph API: -You can now test whether your bot is connected to Facebook correctly by sending a message via the Facebook Page you associated with your new Facebook app. +1. Go to [Meta for Developers](https://developers.facebook.com). +1. Go to the app you created for your bot. +1. Under **Settings**, select **Advanced**. +1. Select **Upgrade API version**, then switch **Upgrade All Calls** to version 4.0. -1. Navigate to your Facebook Page. +Test the connection by following the steps described in the [Test your bot in Facebook](#test-your-bot-in-facebook) section. -1. Click **Add a Button** button. +## Test your bot in Facebook - ![Add a button](media/bot-service-channel-connect-facebook/add-button.png) +You can now test whether your bot is connected to Facebook correctly by sending a message via the Facebook Page you associated with your new Facebook app. -1. Select **Contact You** and **Send Message** and click **Next**. +1. Go Facebook and switch to the profile for your page. +1. Select more actions (**…**), then **Add Action Button**. + 1. In the **Customize your action button** dialog, select **Try it** and follow the instructions. + 1. On the **Action Button** page under **Get people to contact you**, select **Send Message**. + 1. Select **Next**, complete the dialog and save your changes. +1. Switch back to your personal profile. +1. Go to your page, and select **Message** to test the connection to your bot. - ![Add a button](media/bot-service-channel-connect-facebook/button-settings.png) +You can also test this feature using the [sample bot for the Facebook adapter](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/61.facebook-adapter) by populating the **appsettings.json** file with the same values described in the previous steps. -1. When asked **Where would you like this button to send people to?** select **Messenger** and click **Finish**. +## Additional information - ![Add a button](media/bot-service-channel-connect-facebook/button-settings-2.png) +See the Bot Framework C# [Facebook-events sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/23.facebook-events) for a sample bot that supports Facebook Messenger communication. -1. Hover over the new **Send Message** button that is now shown on your Facebook Page and click **Test Button** from the pop-up menu. This will start a new conversation with your app via Facebook Messenger, which you can use to test messaging your bot. Once the message is received by your bot, it will send a message back to you, echoing the text from your message. +For Facebook Messenger documentation, see: -You can also test this feature using the [sample bot for the Facebook adapter](https://aka.ms/csharp-61-facebook-adapter-sample) by populating the appSettings.json file with the same values described in the steps above. +- [Messenger platform documentation](https://developers.facebook.com/docs/messenger-platform). +- [Pre-launch guidelines](https://developers.facebook.com/docs/messenger-platform/product-overview/launch#app_public) +- [Quick start](https://developers.facebook.com/docs/messenger-platform/guides/quick-start) +- [Setup guide](https://developers.facebook.com/docs/messenger-platform/guides/setup) -## See also +For Facebook Workplace documentation, see: -- **Sample code**. Use the [Facebook-events](https://aka.ms/facebook-events) sample bot to explore the bot communication with Facebook Messenger. +- [Workplace Help Center](https://workplace.facebook.com/help/work/) +- [Workplace Developer Documentation](https://developers.facebook.com/docs/workplace) diff --git a/articles/bot-service-channel-connect-groupme.md b/articles/bot-service-channel-connect-groupme.md index ab8931142..96d38a585 100644 --- a/articles/bot-service-channel-connect-groupme.md +++ b/articles/bot-service-channel-connect-groupme.md @@ -1,45 +1,66 @@ --- -title: Connect a bot to GroupMe - Bot Service -description: Learn how to configure a bot's connection to GroupMe. -keywords: bot channel, GroupMe, create GroupMe, credentials -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +title: Connect a Bot Framework bot to GroupMe +description: Learn how to configure bots to connect to the GroupMe channel and to communicate with users via the GroupMe group messaging app. +keywords: bot channel, GroupMe, create GroupMe +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to GroupMe -You can configure a bot to communicate with people using the GroupMe group messaging app. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -[!INCLUDE [Channel Inspector intro](~/includes/snippet-channel-inspector.md)] +You can configure your bot to communicate with people through GroupMe. This article describes how to create a GroupMe app using the GroupMe developers site and connect your bot to your GroupMe app in Azure. -## Sign up for a GroupMe account +## Prerequisites -If you don't have a GroupMe account, [sign up for a new account](https://web.groupme.com/signup). +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A bot published to Azure that you want to connect to GroupMe. +- A GroupMe account. If you don't have a GroupMe account, [sign up for a new account](https://web.groupme.com/signup). ## Create a GroupMe application -[Create a GroupMe application](https://dev.groupme.com/applications/new) for your bot. +1. Go to the GroupMe developers site and sign in to your account. +1. [Create a GroupMe application](https://dev.groupme.com/applications/new) for your bot. + 1. Enter a name for your application. + 1. For the **Callback URL**: + - For a global bot, enter `https://groupme.botframework.com/Home/Login`. + - For a regional bot, enter following url according to the selected region: -Use this callback URL: `https://groupme.botframework.com/Home/Login` + | Region | Callback URL | + |:-|:-| + | Europe| https://europe.groupme.botframework.com/Home/Login | + | India | https://india.groupme.botframework.com/Home/Login | + 1. Enter the rest of the information requested. + 1. Agree to GroupMe's terms of use and branding standards. + 1. Select **Save** to complete creation of the app. -![Create app](~/media/channels/GM-StepApp.png) +### Get your app credentials -## Gather credentials +Copy the following information from the **Details** tab for your application: -1. In the **Redirect URL** field, copy the value after **client_id=**. -2. Copy the **Access Token** value. +1. Copy your _client ID_. This is the value for the `client_id` query parameter in the **Redirect URL**. + For example, if your redirect URL is `https://oauth.groupme.com/oauth/authorize?client_id=my-client-id`, then your client ID is `my-client-id`. +1. Copy your **Access Token** value. -![Copy client ID and access token](~/media/channels/GM-StepClientId.png) +## Configure your bot in Azure +To complete this step, you'll need your GroupMe application credentials from the previous step. -## Submit credentials +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **GroupMe**. +1. In **GroupMe Channel Configuration**, enter the information you copied in the previous steps. + 1. Under **GroupMe credentials**, enter the **Access Token** and **Client ID** for your GroupMe app. + 1. Optionally, enable direct messaging in the channel. For more information, see [What is a Direct Message in GroupMe?](https://support.microsoft.com/office/what-is-a-direct-message-in-groupme-197fb53e-9699-4e14-a35e-d6fa12ea9875) + 1. Select **Apply**. -1. On dev.botframework.com, paste the **client_id** value you just copied into the **Client ID** field. -2. Paste the **Access Token** value into the **Access Token** field. -2. Click **Save**. +## Additional information -![Enter credentials](~/media/channels/GM-StepClientIDToken.png) +For more about applications in GroupMe, see [GroupMe's API Documentation](https://dev.groupme.com/). diff --git a/articles/bot-service-channel-connect-kik.md b/articles/bot-service-channel-connect-kik.md deleted file mode 100644 index 9d1368d1e..000000000 --- a/articles/bot-service-channel-connect-kik.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Connect a bot to Kik - Bot Service -description: Learn how to configure a bot's connection to Kik. -keywords: connect a bot, bot channel, Kik bot, credentials, configure, phone -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 ---- - -# Connect a bot to Kik - -You can configure your bot to communicate with people using the Kik messaging app. - -## Install Kik on your phone - -If you don't have Kik installed on your phone, install it via your phone's app store or at the Kik website. You'll need to use an existing Kik user account or sign up for an new account. - -![Kik sign up](./media/channels/kik-signup.png) - -## Log into the dev portal with your mobile phone - -Use your mobile phone to log into the Kik portal. When prompted, _Open this page in "Kik"?_ select **Open**. - -![Kik dev portal](./media/channels/kik-dev-portal.png) - -## Follow the bot setup process - -Give your bot a name. - -![Set up bot](./media/channels/kik-phone.png) - -## Gather credentials - -On the Configuration tab, copy the **Name** and **API key**. - -![Copy bot information](./media/channels/kik-configure.png) - -## Submit credentials - -![Paste credentials](./media/channels/kik-creds.png) - -Click **Submit Kik Credentials**. - -## Enable the bot - -Check **Enable this bot on Kik**. Then click **I'm done configuring Kik**. - -When you have completed these steps, your bot will be successfully configured to communicate with users in Kik. diff --git a/articles/bot-service-channel-connect-line.md b/articles/bot-service-channel-connect-line.md index 7f9327766..9e809aed4 100644 --- a/articles/bot-service-channel-connect-line.md +++ b/articles/bot-service-channel-connect-line.md @@ -1,122 +1,101 @@ --- title: Connect a bot to LINE - Bot Service -description: Learn how to configure a bot's connection to LINE. +description: Learn how to connect bots to LINE. See how to configure bots to communicate with people through the LINE app. keywords: connect a bot, bot channel, LINE bot, credentials, configure, phone -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 1/7/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to LINE -You can configure your bot to communicate with people through the LINE app. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## Log into the LINE console +You can configure your bot to communicate with people through the LINE app. This article describes how to create a LINE channel using the LINE Developers Console, connect your bot to your LINE channel in Azure, and test your bot in the LINE mobile app. -Log into the [LINE developer console](https://developers.line.biz/console/register/messaging-api/provider/) of your LINE account, using *Log in with Line*. +## Prerequisites -> [!NOTE] -> If you haven't already, [download LINE](https://line.me/), then go to your settings to register your email address. +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure. +- A mobile device with the LINE app and a QR reader. -### Register as a developer +## Create a channel in the LINE Developers Console -If this is your first time on the LINE developer console, enter your name and email address to create a developer account. +To build bots with LINE, you need to create a channel in the LINE Developers Console. Follow the steps in the documentation below that are relevant to you before continuing: -![LINE screenshot register developer](./media/channels/LINE-screenshot-1.png) +- If you already have a LINE Official account: [Adding a channel to your existing LINE Official Account](https://developers.line.biz/en/docs/messaging-api/getting-started/#using-oa-manager). +- If you don't have a LINE account: [Creating a channel on the LINE Developers Console](https://developers.line.biz/en/docs/messaging-api/getting-started/#using-console). -## Create a new provider +## Get values from your channel settings -First, create a provider for your bot if you don't already have one set up. The provider is the entity (individual or company) that offers your app. +Once you've confirmed your channel settings, you'll be directed to your channel's **Basic settings** page. -![LINE screenshot create provider](./media/channels/LINE-screenshot-2.png) - -## Create a Messaging API channel - -Next, create a new Messaging API channel. - -![LINE screenshot channel type](./media/channels/LINE-channel-type-selection.png) - -Create a new Messaging API channel by clicking on the green square. - -![LINE screenshot create channel](./media/channels/LINE-create-channel.png) - -The name cannot include "LINE" or some similar string. Fill out the required fields and confirm your channel settings. - -![LINE screenshot channel settings](./media/channels/LINE-screenshot-4.png) - -## Get necessary values from your channel settings - -Once you've confirmed your channel settings, you'll be directed to a page similar to this. - -![LINE screenshot channel page](./media/channels/LINE-screenshot-5.png) - -Click on the channel you created to access your channel settings, and scroll down to find the **Basic information > Channel secret**. Save that somewhere for a moment. Verify the Available features include `PUSH_MESSAGE`. - -![LINE screenshot channel secret](./media/channels/LINE-screenshot-6.png) - -Then, scroll farther to **Messaging settings**. There, you will see a **Channel access token** field, with an *issue* button. Click that button to get your access token, and save that for the moment as well. - -![LINE screenshot channel token](./media/channels/LINE-screenshot-8.png) +1. Scroll down to find the **Channel secret**. Copy the value and save it for later steps. +1. Scroll back up and select the **Messaging settings** tab. +1. At the bottom of the page you'll see a **Channel access token (long lived)** field, with an **Issue** button. Select that button to get your access token. +1. Copy and save the access token for later steps. ## Connect your LINE channel to your Azure bot -Log in to the [Azure portal](https://portal.azure.com/) and find your bot, and click on **Channels**. - -![LINE screenshot azure settings](./media/channels/LINE-channel-setting-2.png) +After obtaining the values above, you're ready to connect your Azure bot to LINE in the Azure portal. -There, select the LINE channel and paste the channel secret and access token from above into the appropriate fields. Be sure to save your changes. - -Copy the custom webhook URL that Azure gives you. - -![LINE screenshot azure settings](./media/channels/LINE-channel-setting-1.png) +1. Sign in to the [Azure portal](https://portal.azure.com/) and find your bot. Then select **Channels**. +1. Select **LINE** from the **Available channels** list. +1. Enter the **Channel Secret** and **Channel Access Token** you saved earlier. Then select **Apply**. +1. If your bot is successfully connected, the **Webhook URL** will appear. Copy and save the URL for later steps. ## Configure LINE webhook settings -Next, go back to the LINE developer console and paste the webhook URL from Azure into the **Message settings > Webhook URL**, and click **Verify** to verify the connection. If you just created the channel in Azure, it may take a few minutes to take effect. +After connecting your channel in Azure and obtaining your webhook URL, return to the LINE Developers Console to configure LINE webhook setting. -Then, enable **Message settings > Use webhooks**. +1. Go back to the [LINE Developers console](https://developers.line.biz/console/). +1. Select the channel you created earlier from **Recently visited channels**. +1. Select the **Messaging API** setting and scroll down to **Webhook settings**. Enter the **Webhook URL** from Azure and select **Update**. +1. Select the **Verify** button under the URL. A success message will appear if the webhook URL is properly configured. +1. Then enable **Use webhook**, shown below: -> [!IMPORTANT] -> In LINE Developer Console, you must first set the webhook URL, and only then set **Use webhooks = Enabled**. First enabling webhooks with an empty URL will not set the enabled status, even though the UI may say otherwise. + :::image type="content" source="./media/channels/LINE-webhook-settings.png" alt-text="LINE Webhook settings"::: -After you added a webhook URL and then enabled webhooks, make sure to reload this page and verify that these changes were set correctly. + > [!IMPORTANT] + > In LINE Developers Console, set the webhook URL before you enable **Use webhook**. Enabling webhooks with an empty URL won't set the enabled status, even though the UI may say otherwise. -![LINE screenshot webhooks](./media/channels/LINE-screenshot-9.png) +1. After adding a webhook URL and enabling **Use webhook**, reload this page and verify that the changes were set correctly. ## Test your bot -Once you have completed these steps, your bot will be successfully configured to communicate with users on LINE and is ready to test. +Once you've completed these steps, your bot will be successfully configured to communicate with users on LINE. The steps below explain how to test your bot. ### Add your bot to your LINE mobile app -In the LINE developer console, navigate to the settings page and you will see a QR code of your bot. - -In the Mobile LINE app, go to the right most navigation tab with three dots [**...**] and tap on the QR code icon. +To test your bot, you need to use the LINE mobile app. -![LINE screenshot mobile app](./media/channels/LINE-screenshot-12.jpg) - -Point the QR code reader at the QR code in your developer console. You should now be able to interact with your bot in your mobile LINE app and test your bot. +1. Scroll up in the **Messaging API** tab to see the bot's QR code. +1. Using a mobile device with the LINE app installed, scan the QR code and select the link that appears. +1. You should now be able to interact with your bot in your mobile LINE app and test your bot. ### Automatic messages -When you start testing your bot, you may notice the bot sends unexpected messages that are not the ones you specified in the `conversationUpdate` activity. Your dialog may look something like this: - -![LINE screenshot conversation](./media/channels/LINE-screenshot-conversation.jpg) +When you start testing your bot, it may send unexpected messages that aren't the ones you specified in the `conversationUpdate` activity. -To avoid sending these messages, you need to switch off the Auto-response messages. +To avoid sending these messages, take the following steps: -![LINE screenshot auto response](./media/channels/LINE-screenshot-10.png) +1. Go to the LINE Developers Console and select your channel. Then select the **Messaging API** tab. +1. Scroll down to the **LINE Official Account features** section. Find **Auto-reply messages** and select the **Edit** link. +1. A new page titled **Response settings** will open up. Under **Detailed settings** set **Auto-response** to *Disabled*. -Alternatively, you can choose to keep these messages. In this case, it may be a good idea to click “Set message” and edit it. + :::image type="content" source="./media/channels/LINE-detailed-settings.png" alt-text="LINE Detailed settings"::: -![LINE screenshot set auto response](./media/channels/LINE-screenshot-11.png) +1. Alternatively, you can choose to keep these messages. Select **Auto-response message settings** to edit the auto response message. -## Troubleshooting +## Additional information -* In case your bot is not responding to any of your messages at all, navigate to your bot in Azure portal, and choose Test in Web Chat. - * If the bot works there, but does not respond in LINE, reload your LINE Developer Console page and repeat the webhook instructions above. Be sure you set the **Webhook URL** before enabling webhooks. - * If the bot doesn't work in Web Chat, debug the issue for your bot then come back and finish configuring your LINE channel. +### Troubleshooting +- If your bot isn't responding to any of your messages, go to your bot in Azure portal, and select **Test in Web Chat**. + - If the bot works there but doesn't respond in LINE, reload your LINE Developer Console page and repeat the webhook instructions above. Be sure you set the **Webhook URL** before enabling webhooks. + - If the bot doesn't work in Web Chat, debug the bot issue and then finish configuring your LINE channel. diff --git a/articles/bot-service-channel-connect-m365.md b/articles/bot-service-channel-connect-m365.md new file mode 100644 index 000000000..4417bc430 --- /dev/null +++ b/articles/bot-service-channel-connect-m365.md @@ -0,0 +1,64 @@ +--- +title: Add a chatbot to M365 (preview) +description: Connect your bot to M365 so people in your organization can interact with it in Microsoft Copilot Studio preview. +keywords: "Publish, channel, M365" +ms.topic: how-to +author: KendalBond007 +ms.author: jameslew +ms.reviewer: yiba +manager: iawilt +ms.custom: + - publication + - authoring + - ceX + - bap-template + - evergreen +ms.collection: virtual-agent +ms.service: copilot-studio +--- + +# Connect a Bot to Microsoft 365 + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +You can configure your bot to communicate with people in Microsoft 365 applications. This article describes how to connect your bot to your M365 Extensions Channel in Azure, and then test your bot in Outlook. You'll be able to run your [message extensions](/microsoftteams/platform/messaging-extensions/what-are-messaging-extensions?tabs=dotnet) in Outlook and other Microsoft 365 channels with the help of this channel. + +## Prerequisites + +- An Azure subscription. If you don't already have one, create a free account before you begin. + +- A bot published to Azure. + +- A developer tenant in Outlook with custom app uploading or sideloading enabled. For more information, see [Prepare your Microsoft 365 tenant](/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant). + +## Configure your bot in Azure + +1. Open the [Azure portal](https://ms.portal.azure.com/#home). + +2. Open the Azure Bot resource blade for your bot. + +3. Open **Channels** and select **M365 Extensions Channel**: + + 1. Select **Apply**. + +## Test your bot in Outlook + +To test your bot in Outlook, you need to publish the app in Teams and update the [Teams developer manifest](/microsoftteams/platform/resources/schema/manifest-schema) schema version to greater than 1.13 to enable your Teams message extension to run in Outlook. + +For instructions on how to publish your app, see the Teams overview of how to [Distribute your Microsoft Teams app](/microsoftteams/platform/concepts/deploy-and-publish/apps-publish-overview). It and the associated articles cover how to: + +- Choose and configure install options for your bot. + +- Create your Teams app manifest, icon, and package. + +- Upload your app to Teams. + +- Publish your app to your org or to the Teams store. + +For more about Message extensions in Outlook, see [Extend a Teams message extension across Microsoft 365 - Teams | Microsoft Learn](/microsoftteams/platform/m365-apps/extend-m365-teams-message-extension?tabs=manifest-teams-toolkit) + +## Additional information + +- For more about creating bots for Teams, see [Bots in Microsoft Teams](/microsoftteams/platform/bots/what-are-bots). + +- To provide feedback and find more resources, see [Microsoft Teams developer community channels](/microsoftteams/platform/feedback). diff --git a/articles/bot-service-channel-connect-search.md b/articles/bot-service-channel-connect-search.md new file mode 100644 index 000000000..51a1cffaa --- /dev/null +++ b/articles/bot-service-channel-connect-search.md @@ -0,0 +1,320 @@ +--- +title: Connect a Bot Framework bot to Search +description: Learn how to configure bots to use Search to communicate with people. See how to connect bots to Search in Azure. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +monikerRange: 'azure-bot-service-4.0' +--- + +# Connect a bot to Search (preview) + +This article describes how to create a custom federated search provider (power by your bot) and connect it to the Search channel. Once the tenant administrator enables your provider in their tenant, enterprise user searches, from Office.com, SharePoint.com, and Bing.com, can include results from your custom search provider. + +The Microsoft Federated Search Platform enables you to build custom federated search providers to allow your information to participate in Microsoft Search's Answers & Vertical experiences without the need for merging that information with your Microsoft 365 index. For more information, see [Announcing developer preview of the Microsoft Federated Search Platform](https://techcommunity.microsoft.com/t5/microsoft-search-blog/announcing-developer-preview-of-the-microsoft-federated-search/ba-p/2480763) and [Dynamics 365 federation search (preview)](/microsoftsearch/manage-dynamics365). + +> [!NOTE] +> The Search channel is in private preview. To request access, use the [Microsoft Search Developer Private Preview form](https://aka.ms/SearchDevPrivatePreview). In question 7, select **Federated Search**. + +The following steps are required to connect your bot to the Search channel. These steps are described in more detail later in this article. + +1. Implement your bot to work as a search provider. +1. If your bot will require users to be signed in: + 1. In the Azure portal, expose the bot API to the search platform. + 1. In your bot code, use the generated scope URI to generate a user token. +1. Deploy your bot to Azure. +1. Add the Search channel to your bot. +1. Ask your IT administrator to review your bot registration and publish your bot in the tenant. + +> [!TIP] +> We recommend that you enable the search provider in a test tenant before you enable it in production. + +## Prerequisites + +- Knowledge of [Basics of the Bot Framework Service](v4sdk/bot-builder-basics.md) and how to [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md). +- The bot to connect to the channel. +- If you don't have an Azure account, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. + +You can implement your bot in any of the languages supported by the Bot Framework SDK. +This article uses the C# [federated search bot](https://github.com/Azure/federated-search-preview/tree/main/Samples/csharp_dotnetcore#01.fedetated-search-provider) as an example. + +## Expose the bot API to Search + +> [!TIP] +> This step is only needed if your bot requires access to protected user resources. + +In some business workflows, a bot might require user credentials to perform an action on the user's behalf. +To create a single sign-on (SSO) experience for your bot in the Search channel, you must allow the search platform to secure an access token from Microsoft Entra ID on behalf of the user. + +To generate a scope URI and application ID for your bot: + +1. Go to the Azure portal. +1. If you don't already have a bot resource, create an Azure Bot resource. +1. Go to the **Microsoft Entra ID** service. +1. Go to the **App registrations** pane. +1. Select the application associated with your bot. +1. Go to the **Expose an API** pane. +1. Select **Add a scope**. + 1. On the **Add a scope** pane, we recommend that you keep the autogenerated **Application ID URI**. Select **Save and continue**. + 1. Enter a **Scope name**. + 1. For **Who can consent?**, **Admins and users** is preferred, but both options will work. + 1. Enter an **Admin consent display name** and an **Admin consent description**. + 1. Optionally, enter a **User consent display name** and a **User consent description**. + 1. Verify that **State** is set to **Enabled**. + 1. Select **Add scope**. +1. Select **Add a client application**. + 1. In the **Add a client application** pane, set **Client ID** to `81473081-50b9-469a-b9d8-303109583ecb`, the Search platform's client ID. + 1. Under **Authorized scopes**, select the scope URI you created in the previous step. + 1. Select **Add application**. +1. Go to the **Overview** pane. Copy the **Application ID URI**. You'll need this when you register your bot in the Search channel. + +## Implement your bot + +The Search channel sends each user query to your bot as an invoke activity with a name of "application/search". Your bot returns the query results in the invoke response. Use the Adaptive Card format for the query results sent back to the Search channel. + +1. Update any Bot Framework and Adaptive Cards packages in your project to the latest version. +1. Optionally, [add authentication code](#add-authentication) to generate a user token. +1. [Implement a data search method](#query-each-data-store) for each data source to include. +1. Generate the Adaptive Card to [display the results](#display-the-search-results). + +### Get the Search platform trace ID + +The Search platform assigns a unique trace ID to each query it sends to your bot. The platform adds this to the invoke activity's channel data. You might decide to log the trace ID of the request. Use the channel data's `traceId` property to get the trace ID. + +In the [federated search sample](https://github.com/Azure/federated-search-preview/tree/main/Samples/csharp_dotnetcore/01.fedetated-search-provider), the `SearchHelper.GetSearchTraceId` method demonstrates how to get the trace ID from the invoke activity. + +### Add authentication + +If you [exposed your bot API to Search](#expose-the-bot-api-to-search) and you requested authentication when you [connected your bot to Search](#connect-your-bot-to-search), you can get the user authentication token from the activity's channel data. + +The channel data's `authorizations` property can contain a list of authentication tokens. If you expose your bot API to Search, the list will contain the on-behalf-of token. The token in the list will have the following structure: + +| Property Name | Type | Description | +|:-|:-|:-| +| authType | integer | The authentication token type: `0` for unknown or default, or `2` for on-behalf-of token. | +| token | string | The authentication token itself. | + +In the [federated search sample](https://github.com/Azure/federated-search-preview/tree/main/Samples/csharp_dotnetcore/01.fedetated-search-provider): + +- The `SearchBotAuthenticationToken` class and the `AuthenticationTypes` enumeration represent this information. +- The `SearchHelper.GetSearchOboToken` method demonstrates how to get the token from the invoke activity. + +Once you have the token, you can use it when you request any protected resources for the user. For information on using on-behalf-of tokens, see [Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow](/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow). + +### Query each data store + +The Search channel sends a query to the bot as an `invoke` activity, with the query details in the activity's `value` property, which represents a JSON object with the following structure: + +| Property Name | Type | Description | +|:-|:-|:-| +| queryText | string | The query text. | +| kind | string | The _kind_ of query: "search" when results will be displayed in a custom vertical tab, or "searchAnswer" when a result will be displayed as an answer in the **All** tab. | +| queryOptions | object | Additional query options used for pagination. | +| queryOptions.skip | integer | The index of the first result to send. | +| queryOptions.top | integer | The maximum number of results to send. | + +You return the search results in the invoke response: + +- Always set the invoke response object's `Status` property to `200`, which indicates that the network connection is okay. The object's `Body` property has a separate status code. +- The `Body` property represents a JSON object with the following structure: + + | Property Name | Type | Description | + |:-|:-|:-| + | statusCode | integer | An HTTP status code used to indicate whether the bot was able to successfully run the query. | + | type | string | The type of the invoke response, defining the format of the value field. Use "application/vnd.microsoft.search.searchResponse" for search results, or "application/vnd.microsoft.error" for an error message. | + | value | object | A value that corresponds to the value `type`. | + + For error messages, the `value` object contains: + + | Property Name | Type | Description | + |:-|:-|:-| + | code | string | An error code defined by the bot, or `null` if not specified. | + | message | string | An error message, or `null` if not specified. | + + For search results, the `value` object contains: + + | Property Name | Type | Description | + |:-|:-|:-| + | results | array of _search result_ objects | The results, or `null` if none. | + | displayLayouts | array of _display layout_ objects | The display layouts, or `null` if none. | + | totalResultCount | integer | The total results available, if pagination is supported; otherwise, `null`. | + | moreResultsAvailable | Boolean | Indicates whether more results are available. | + + Search result objects contain: + + | Property Name | Type | Description | + |:-|:-|:-| + | value | string | A unique identifier or value for this search result. | + | layoutId | string | The ID of the display layout to use for this result. | + | data.searchResultText | string | The text for this result. | + + Display layout objects contain: + + | Property Name | Type | Description | + |:-|:-|:-| + | layoutId | string | The layout ID. | + | layoutBody | string | The layout body as an Adaptive Cards JSON object. | + +In the [federated search sample](https://github.com/Azure/federated-search-preview/tree/main/Samples/csharp_dotnetcore/01.fedetated-search-provider), the `SearchHelper.RunFederatedSearch` method demonstrates how to get the query information from the invoke activity and how to format the invoke response. + +### Display the search results + +You can create search verticals and result types to customize the search results that users see when they search in SharePoint, Office, and Bing. Verticals make it easier for users to find the information that they have permission to see. For more information, see the [Supported Adaptive Card elements](#supported-adaptive-card-elements) section. + +If your bot receives a query for which it has no response, its reply should contain an empty response. + +## Register your bot in Azure + +To connect your bot to the Search channel, you must have a bot resource provisioned in Azure. +For more information, see how to [Register a bot with Azure](bot-service-quickstart-registration.md) or how to [Deploy your bot in Azure](bot-builder-deploy-az-cli.md). + +## Connect your bot to Search + +The following instructions show you how to connect a bot to Search. + +> [!TIP] +> We recommend that you enable the search provider in a test tenant before you enable it in production. + +1. Go to the Azure portal. +1. Open your bot resource. +1. Open the **Channels (Preview)** pane. +1. Select **Search**. +1. In the **Search Settings** tab, enter information for your bot. + + :::image type="content" source="media/channels/search/configure-search.png" alt-text="Sample of the Search Settings tab"::: + + 1. Under **Search Provider Metadata**, enter the name to display in the search UI. + 1. Under **Trigger Phrases**, define the phrases that represent the queries your bot can answer. + + > [!NOTE] + > For initial releases, only English (en-US) is available. + + - Upload a `.csv` file that contains the phrases. The file should contain one column of data, with no headers. + - From the **Language preference** list, select the language in which the trigger phrases are written. + 1. Under **Authentication**, indicate whether your search provider requires user authentication. + - If authentication is required, enter the authentication URL. Use the **Application ID URI** you copied when you [exposed the API for your bot](#expose-the-bot-api-to-search). + 1. Select **Next**. +1. In the **Verticals** tab, if you want the results from your search provider to show up in its own custom vertical in the search results page, then input the vertical name in the field; otherwise, leave this field empty. Then, select **Next**. + The search results page is for Office.com, SharePoint.com, and Bing.com. +1. In the **Tenant Publishing** tab, review your settings and add publishing information. + 1. Review the search provider name and sample queries. Go back to the previous tabs to change this information, if necessary. + 1. Enter a description of your search provider. + 1. Enter a support contact email. Use the email of a developer or developer group who has access to the search provider. +1. Select **Add** to request approval from your IT administrator. + +## Approve a search provider in a tenant + +The search provider approval in the tenant is made by an IT administrator in the [Search & Intelligence](https://admin.microsoft.com/Adminportal/Home?ref=/MicrosoftSearch) page in the **Microsoft 365 admin center**. + +## Test the connection + +We recommend that you enable the search provider in a test tenant before you enable it in production. + +## Modify a search provider + +You can edit the search provider before you submit it for review by the IT administrator. You might need to do so if your initial request is rejected or your service is deactivated. + +1. In the Azure portal, go to the bot resource that contains the search provider you want to edit. +1. Go to the **Channels (Preview)** pane. +1. Select the **Search** channel, and select **Edit**. + 1. Azure displays the **Search Channel** pane. In this pane, you can edit your settings. + 1. To modify the triggering phrases, download the file, edit it locally, and upload the file. + 1. Once you finish your edits, select **Add** again to submit search provider for review by the IT administrator. + +## Delete a search provider + +Your search provider will be deleted if you remove the Search channel from the bot resource. + +To remove the Search channel from your bot: + +1. In the Azure portal, go to your bot resource. +1. Go to the **Channels (Preview)** pane. +1. Select the Search channel. +1. At the top of the **Search Channel** pane, select **Delete channel**. +1. Select **Yes** to confirm the operation. + +To delete your bot resource: + +1. In the Azure portal, go to your bot resource. +1. If you haven't already done so, remove the Search channel from your bot. +1. At the top of the **Overview** pane, select **Delete**. +1. Select **OK** to confirm the operation. + +## Additional information + +The search channel uses federated search and the Adaptive Cards schema: + +For more information about the Adaptive Card schema, see [Adaptive Cards for bot developers](/adaptive-cards/getting-started/bots). + +### About trigger phrases + +A trigger phrase is a phrase that the search platform uses to route a query to your custom search provider powered by your bot. Federated search forwards a user's utterance to your search provider when the utterance is a close match to one of the trigger phrases. + +> [!TIP] +> If more than one search provider is available, federated search chooses only one, based on the trigger phrases provided and the user's query. + +As an example, think of a bot that manages flight schedules and status. + +1. Think of a few common ways a user would refer to or make use of your bot. Be sure to distinguish your bot from others. + + Instead of a generic term, such as "timetable" that can apply to schools and TV programs, use more specific phrases, such as "flight time table" and "flight schedule". + +1. Include diverse phrases that cover the scope of your bot's features, such as departure time and current status. + + For example, include queries about: arrival or departure times and airports. + +The trigger phrases for such a _flight schedule and status_ bot might include: + +- Flight timetable +- Flight status +- Flight 675 departure time +- When will my flight depart +- Flight 468 arrival time +- Seattle Tacoma flight status +- Heathrow flight status + +As another example, the trigger phrases for a _weather forecast_ bot might include: + +- Local weather forecast +- Weather info +- Tomorrow's weather +- 10-day weather forecast +- Today's high +- Today's chance of rain +- Will it snow tomorrow +- Wind speed tomorrow +- Is it windy outside +- London weather + +### Supported Adaptive Card elements + +A subset of the Adaptive Card schema is supported in federated search. +For information about formatting your search results, see [Customize the search results page](/microsoftsearch/customize-search-page). + +Support includes the following Adaptive Card elements: [TextBlock](https://adaptivecards.io/explorer/TextBlock.html), [RichTextBlock](https://adaptivecards.io/explorer/RichTextBlock.html), [Image](https://adaptivecards.io/explorer/Image.html), [ColumnSet](https://adaptivecards.io/explorer/ColumnSet.html), [ImageSet](https://adaptivecards.io/explorer/ImageSet.html), and [FactSet](https://adaptivecards.io/explorer/FactSet.html). For more information, see Microsoft Search's [Manage search result layouts](/microsoftsearch/customize-results-layout) and the Adaptive Cards [Schema Explorer](https://adaptivecards.io/explorer/). + +You can author each card directly as JSON, or you can use the [AdaptiveCards](https://www.nuget.org/packages/AdaptiveCards/) NuGet package. + +### Federated search doesn't support HTML + +> [!IMPORTANT] +> Federated search won't render Adaptive Card text that contains HTML. + +The Search platform doesn't include an HTML parser. +However, you can eliminate some of the tags and use the [Html2Markdown](https://www.nuget.org/packages/Html2Markdown) NuGet package to convert HTML to Markdown: + +1. Remove `` and `` elements. +1. Replace `
` and `
` elements with paragraph (`

`) elements. +1. Convert the remaining HTML to Markdown. + +## Next steps + +- For information about channel support in the Bot Connector Service, see [Connect a bot to channels](bot-service-manage-channels.md). +- For information about building bots, see [How bots work](v4sdk/bot-builder-basics.md) and the [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md) quickstart. +- For information about deploying bots, see [Deploy your bot](bot-builder-deploy-az-cli.md) and [Set up continuous deployment](bot-service-build-continuous-deployment.md). diff --git a/articles/bot-service-channel-connect-skype.md b/articles/bot-service-channel-connect-skype.md index ab7ceb86f..01460d7ab 100644 --- a/articles/bot-service-channel-connect-skype.md +++ b/articles/bot-service-channel-connect-skype.md @@ -1,59 +1,60 @@ --- -title: Connect a bot to Skype - Bot Service -description: Learn how to configure a bot for access through the Skype interface. +title: Connect a Bot Framework bot to Skype +description: Learn how to configure bots to connect to Skype and communicate with users via Skype. keywords: skype, bot channels, configure skype, publish, connect to channels -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 10/11/2018 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms-custom: abs-meta-21q1 +ms.custom: + - evergreen --- # Connect a bot to Skype -Skype keeps you connected with users through instant messaging, phone, and video calls. Extend this functionality by building bots that users can discover and interact with through the Skype interface. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] ->[!NOTE] -> As of October 31, 2019 the Skype channel no longer accepts new Bot publishing requests. This means that you can continue to develop bots using the Skype channel, but your bot will be limited to 100 users. You will not be able to publish your bot to a larger audience. Current Skype bots will continue to run uninterrupted. Read more about [why some features are not available in Skype anymore](https://support.skype.com/faq/fa12091/why-are-some-features-not-available-in-skype-anymore). - -To add the Skype channel, open the bot in the [Azure Portal](https://portal.azure.com/), click the **Channels** blade, and then click **Skype**. - -![Add Skype channel](~/media/channels/skype-addchannel.png) - -This will take you to the **Configure Skype** settings page. - -![Configure Skype channel](~/media/channels/skype_configure.png) - -You need to configure settings in **Web control**, **Messaging**, **Calling**, **Groups** and **Publish**. Let's go over them one by one. +This article describes how to configure a bot already connected to Skype. -## Web control - -To embed the bot into your website, click the **Get embed code** button from the **Web control** section. This will direct you to the Skype for Developers page. Follow the instructions there to get the embed code. - -## Messaging - -This section configures how your bot sends and receives messages in Skype. - -## Calling - -This section configures the calling feature of Skype in your bot. You can specify whether **Calling** is enabled for your bot and if enabled, whether IVR functionality or Real Time Media functionality is to be used. +>[!NOTE] +> As of May 12, 2023 Skype bots are again supported and will be going forward. -## Groups +## Prerequisites -This section configures whether your bot can be added to a group and how it behaves in a group for messaging and is also used to enable Group Video Calls for Calling bots. +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure and previously connected to Skype. -## Publish +## Configure your bot in Azure -This section configures the publish settings of your bot. All fields labeled with a * are required fields. +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **Skype**. +1. Use the tabs to configure the channel: + 1. **Messaging** controls how your bot sends and receives massages in Skype. + 1. **Calling** controls whether calling is enabled. + 1. **Groups** controls whether your bot can be added to a group. + 1. Select **Save** and accept the **Terms of Service** to connect the Skype channel to your bot. -Bots in **Preview** are limited to 100 contacts. If you need more than 100 contacts, submit your bot for review. Clicking **Submit for Review** will automatically make your bot searchable in Skype if accepted. If your request cannot be approved, you will be notified as to what you need to change before it can be approved. +## Test your bot in Skype -> [!TIP] -> If you are wanting to submit your bot for review, keep in mind it must meet the [skype certification checklist](https://github.com/Microsoft/skype-dev-bots/blob/master/certification/CHECKLIST.md) before it will be accepted. +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select the **Add to Skype** link. An **Add Bot to Contacts** page will open in your browser. + 1. Review the information on the page. + 1. Select **Add to Contacts**. + 1. If prompted, sign-in to Skype and select **Add to Contacts** again. + 1. If prompted, allow the site to open Skype. +1. Skype will open and display a page that describes your bot. + 1. Select **Get Started**. On the next page, select **Send message**. + 1. You can now interact with your bot in Skype. -After finishing configuration, click **Save** and accept the **Terms of Service**. The Skype channel is now added to your bot. +## Get a Web control embed code -## Next steps +To embed the bot into your website, you need an embed code. To get your embed code: -* [Skype for Business](bot-service-channel-connect-skypeforbusiness.md) +1. Go to the **Channels** blade for your bot resource. +1. Select **Get bot embed codes**. +1. Azure displays a list of **Bot Embed Codes**. Copy the embed code for **Skype**. diff --git a/articles/bot-service-channel-connect-skypeforbusiness.md b/articles/bot-service-channel-connect-skypeforbusiness.md deleted file mode 100644 index 775695fc0..000000000 --- a/articles/bot-service-channel-connect-skypeforbusiness.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Connect a bot to Skype for Business - Bot Service -description: Learn how to connect a bot with the Skype for Business tenant. -keywords: skype for business, bot channels -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 ---- - -# Connect a bot to Skype for Business (Preview) - -Skype for Business Online keeps you connected with co-workers and business partners through instant messaging, phone, and video calls. Extend this functionality by building bots that users can discover and interact with through the Skype for Business interface. - -> [!IMPORTANT] -> **Skype for Business channel of Bot Framework was deprecated on June 30, 2019.** -> -> Skype for Business channel stopped accepting new bots on June 30, 2019. Existing bots continued operating through October 31, 2019. The channel is currently being deprecated, and no production loads should be using it. Microsoft Teams is the preferred communication tool from Microsoft. Learn how to [connect your bot to Microsoft Teams](https://msdn.microsoft.com/microsoft-teams/bots). diff --git a/articles/bot-service-channel-connect-slack.md b/articles/bot-service-channel-connect-slack.md index a51265443..4fb98aed2 100644 --- a/articles/bot-service-channel-connect-slack.md +++ b/articles/bot-service-channel-connect-slack.md @@ -1,314 +1,153 @@ --- -title: Connect a bot to Slack - Bot Service -description: Learn how to configure a bot's connection to Slack. +title: Connect a Bot Framework bot to Slack +description: Learn how to configure bots to connect to the Slack channel and communicate with users via Slack. keywords: connect a bot, bot channel, Slack bot, Slack messaging app, slack adapter author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/09/2019 +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to Slack -There are two ways in which you can confgure Slack messaging app: -- Use Azure Bot Service portal to connect your bot -- Use the Slack adapter +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## [Azure Bot Service Portal](#tab/abs) -## Create a Slack application for your bot +You can configure your bot to communicate with people through a Slack app. This article describes how to create a Slack app using Slack, connect your bot to your Slack app in Azure, and test your bot in Slack. -Log into [Slack](https://slack.com/signin) and then go to [create a Slack application](https://api.slack.com/apps) channel. - -![Set up bot](~/media/channels/slack-NewApp.png) - -## Create an app and assign a Development Slack team - -Enter an App Name and select a Development Slack Team. If you are not already a member of a Development Slack Team, [create or join one](https://slack.com/). - -![Create app](~/media/channels/slack-CreateApp.png) - -Click **Create App**. Slack will create your app and generate a Client ID and Client Secret. - -## Add a new Redirect URL - -Next you will add a new Redirect URL. - -1. Select the **OAuth & Permissions** tab. -2. Click **Add a new Redirect URL**. -3. Enter [https://slack.botframework.com](https://slack.botframework.com). -4. Click **Add**. -5. Click **Save URLs**. - -![Add Redirect URL](~/media/channels/slack-RedirectURL.png) - -## Create a Slack Bot User - -Adding a Bot User allows you to assign a username for your bot and choose whether it is always shown as online. - -1. Select the **Bot Users** tab. -2. Click **Add a Bot User**. - -![Create bot](~/media/channels/slack-CreateBot.png) - -Click **Add Bot User** to validate your settings, click **Always Show My Bot as Online** to **On**, and then click **Save Changes**. - -![Create bot](~/media/channels/slack-CreateApp-AddBotUser.png) - -## Subscribe to Bot Events - -Follow these steps to subscribe to six particular bot events. By subscribing to bot events, your app will be notified of user activities at the URL you specify. - -> [!TIP] -> Your bot handle is the name of your bot. To find a bot's handle, -> visit [https://dev.botframework.com/bots](https://dev.botframework.com/bots), -> choose a bot, and record the name of the bot. - -1. Select the **Event Subscriptions** tab. -2. Click **Enable Events** to **On**. -3. In **Request URL**, enter `https://slack.botframework.com/api/Events/{YourBotHandle}`, where `{YourBotHandle}` is your bot handle, without the braces. The bot handle used in this example is **ContosoBot**. - - ![Subscribe Events: top](~/media/channels/slack-SubscribeEvents-a.png) - -4. In **Subscribe to Bot Events**, click **Add Bot User Event**. -5. In the list of events, select these six event types: - * `member_joined_channel` - * `member_left_channel` - * `message.channels` - * `message.groups` - * `message.im` - * `message.mpim` - - ![Subscribe Events: middle](~/media/channels/slack-SubscribeEvents-b.png) - -6. Click **Save Changes**. - - ![Subscribe Events: bottom](~/media/channels/slack-SubscribeEvents-c.png) - -## Add and Configure Interactive Messages (optional) - -If your bot will use Slack-specific functionality such as buttons, follow these steps: - -1. Select the **Interactive Components** tab and click **Enable Interactive Components**. -2. Enter `https://slack.botframework.com/api/Actions` as the **Request URL**. -3. Click the **Save changes** button. - -![Enable messages](~/media/channels/slack-MessageURL.png) - -## Gather credentials - -Select the **Basic Information** tab and scroll to the **App Credentials** section. -The Client ID, Client Secret, and Verification Token required for configuration of your Slack bot are displayed. - -![Gather credentials](~/media/channels/slack-AppCredentials.png) - -## Submit credentials - -In a separate browser window, return to the Bot Framework site at `https://dev.botframework.com/`. - -1. Select **My bots** and choose the Bot that you want to connect to Slack. -2. In the **Channels** section, click the Slack icon. -3. In the **Enter your Slack credentials** section, paste the App Credentials from the Slack website into the appropriate fields. -4. The **Landing Page URL** is optional. You may omit or change it. -5. Click **Save**. - -![Submit credentials](~/media/channels/slack-SubmitCredentials.png) - -Follow the instructions to authorize your Slack app's access to your Development Slack Team. - -## Enable the bot - -On the Configure Slack page, confirm the slider by the Save button is set to **Enabled**. -Your bot is configured to communicate with users in Slack. - -## Create an Add to Slack button - -Slack provides HTML you can use to help Slack users find your bot in the -*Add the Slack button* section of [this page](https://api.slack.com/docs/slack-button). -To use this HTML with your bot, replace the href value (begins with `https://`) with the URL found in your bot's Slack channel settings. -Follow these steps to get the replacement URL. - -1. On [https://dev.botframework.com/bots](https://dev.botframework.com/bots), click your bot. -2. Click **Channels**, right-click the entry named **Slack**, and click **Copy link**. This URL is now in your clipboard. -3. Paste this URL from your clipboard into the HTML provided for the Slack button. This URL replaces the href value provided by Slack for this bot. - -Authorized users can click the **Add to Slack** button provided by this modified HTML to reach your bot on Slack. - -## [Slack adapter](#tab/adapter) -## Connect a bot to Slack using the Slack adapter - -As well as the channel available in the Azure Bot Service to connect your bot with Slack, you can also use the Slack adapter. In this article you will learn how to connect a bot to Slack using the adapter. This article will walk you through modifying the EchoBot sample to connect it to a Slack app. - -> [!NOTE] -> The instructions below cover the C# implementation of the Slack adapter. For instructions on using the JS adapter, part of the BotKit libraries, [see the BotKit Slack documentation](https://botkit.ai/docs/v4/platforms/slack.html). +This article shows how to add a Slack channel to your bot in the Azure portal. For information on how to use a custom channel adapter, see [Additional information](#additional-information). ## Prerequisites -* The [EchoBot sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/02.echo-bot) - -* Access to a Slack workspace with sufficient permissions to create and manage applications at [https://api.slack.com/apps](https://api.slack.com/apps). If you do not have access to a Slack environment you can create a workspace for [free](https://www.slack.com). - -## Create a Slack application for your bot - -Log into [Slack](https://slack.com/signin) and then go to [create a Slack application](https://api.slack.com/apps) channel. - -![Set up bot](~/media/channels/slack-NewApp.png) - -Click the 'Create new app' button. +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A bot published to Azure that you want to connect to Slack. +- Access to a Slack workspace with sufficient permissions to create and manage applications at [https://api.slack.com/apps](https://api.slack.com/apps). If you don't have access to a Slack environment, you can [create a workspace](https://www.slack.com). -### Create an app and assign a development Slack team +## Create an app in Slack -Enter an **App Name** and select a **Development Slack Workspace**. If you are not already a member of a development Slack team, [create or join one](https://slack.com/). +You first create an application in Slack, which generates the information you need to configure the Slack channel for your bot in Azure. -![Create app](~/media/channels/slack-CreateApp.png) +1. Go to [Your Apps](https://api.slack.com/apps) panel and sign in to your Slack account. +1. Select **Create New App**, or **Create an App** if this is your first application. + 1. On the **Create an app** page, select **From scratch**. + 1. On the **Name app & choose workspace** page, for **App Name**, enter the name of your Slack application. + 1. For **Pick a workspace to develop your app in**, select a workspace for your app. + 1. Review and agree to the Slack API terms of service. + 1. Select **Create App**. -Click **Create App**. Slack will create your app and generate a client ID and client secret. +### Add a new redirect URL -### Gather required configuration settings for your bot +Once the app is created, add a redirect URL to your app. -Once your app is created, collect the following information. You will need this to connect your bot to Slack. +1. Select **OAuth & Permissions**. +1. In the resulting pane, under **Redirect URLs**, select **Add a new Redirect URL**. +1. In the input box, enter one of the following redirect URLs and select **Add**. + - For a global bot, enter `https://slack.botframework.com`. + - For a regional bot, enter following url according to the selected region: -1. Note the **Verification Token** and the **Signing Secret** from the **Basic Information** tab and keep them for later to configure your bot settings. + | Region | Redirect URL | + |:-|:-| + | Europe| https://europe.slack.botframework.com | + | India | https://india.slack.botframework.com | +1. Select **Save URLs**. -![Slack tokens](~/media/bot-service-adapter-connect-slack/slack-tokens.png) +### Subscribe to bot events -2. Navigate to the **Install App** page under the **Settings** menu and follow the instructions to install your app into a Slack team. Once installed, copy the **Bot User OAuth Access Token** and, again, keep this for later to configure your bot settings. +After you add the redirect URL, subscribe your app to bot events to have Slack notify your bot of user activities at the URL you specify. +Subscribe to events based on the features your bot will use in Slack. -## Wiring up the Slack adapter in your bot +1. Select **Event Subscriptions**. +1. In the resulting pane, toggle **Enable Events** to **On**. +1. For **Request URL**, enter one of the following request URLs, where `{bot-name}` is the display name for your Azure Bot resource, without the braces. + - For a global bot, enter `https://slack.botframework.com/api/Events/{bot-name}`. + - For a regional bot, enter following url according to the selected region: -### Install the Slack adapter NuGet package + | Region | Request URL | + |:-|:-| + | Europe| `https://europe.slack.botframework.com/api/Events/{bot-name}` | + | India | `https://india.slack.botframework.com/api/Events/{bot-name}` | +1. Under **Subscribe to bot events**, select **Add Bot User Event**, then subscribe to events. For example: + - `member_joined_channel` + - `member_left_channel` + - `message.channels` + - `message.groups` + - `message.im` + - `message.mpim` +1. Select **Save Changes**. -Add the [Microsoft.Bot.Builder.Adapters.Slack](https://www.nuget.org/packages/Microsoft.Bot.Builder.Adapters.Slack/) NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs) +### Enable sending messages to the bot by the users -### Create a Slack adapter class +After you subscribe to bot events, enable users to message your bot. -Create a new class that inherits from the ***SlackAdapter*** class. This class will act as our adapter for the Slack channel and include error handling capabilities (similar to the ***BotFrameworkAdapterWithErrorHandler*** class already in the sample, used for handling other requests from Azure Bot Service). +1. Select **App Home**. +1. In the resulting pane, in the **Show Tabs** section under the **Messages Tab**, enable **Allow users to send Slash commands and messages from the messages tab**. -```csharp -public class SlackAdapterWithErrorHandler : SlackAdapter -{ - public SlackAdapterWithErrorHandler(IConfiguration configuration, ILogger logger) - : base(configuration, logger) - { - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); +### Add and configure interactive messages - // Send a message to the user - await turnContext.SendActivityAsync("The bot encountered an error or bug."); - await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); +Optionally, enable interactive messages. - // Send a trace activity, which will be displayed in the Bot Framework Emulator - await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); - }; - } -} -``` +1. Select **Interactivity & Shortcuts**. +1. For **Request URL**: + - For a global bot, enter `https://slack.botframework.com/api/Actions`. + - For a regional bot, enter following url according to the selected region: -### Create a new controller for handling Slack requests + | Region | Request URL | + |:-|:-| + | Europe| `https://europe.slack.botframework.com/Actions` | + | India | `https://india.slack.botframework.com/Actions` | +1. Select **Save changes**. -We create a new controller which will handle requests from your slack app, on a new endpoing 'api/slack' instead of the default 'api/messages' used for requests from Azure Bot Service Channels. By adding an additional endpoint to your bot, you can accept requests from Bot Service channels, as well as from Slack, using the same bot. +### Copy your app information -```csharp -[Route("api/slack")] -[ApiController] -public class SlackController : ControllerBase -{ - private readonly SlackAdapter _adapter; - private readonly IBot _bot; +You'll need the following information to add the Slack channel to your bot. Always copy and store app credentials in a safe place. - public SlackController(SlackAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } +1. Select **Basic Information**. +1. In the resulting pane, under **App Credentials**, locate **Client ID**, **Client Secret**, and **Signing Secret**. +1. Now, select **OAuth & Permissions**. +1. In the resulting pane, locate the **Scopes** section. Record the **Bot Token Scopes** for your app. - [HttpPost] - [HttpGet] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _adapter.ProcessAsync(Request, Response, _bot); - } -} -``` +## Configure your bot in Azure -### Add Slack app settings to your bot's configuration file +To complete this step, you'll need your Slack application credentials from the previous step. -Add the 3 settings shown below to your appSettings.json file in your bot project, populating each one with the values gathered earlier when creating your Slack app. +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **Slack**. +1. In **Slack Channel Configuration**, enter the information you copied in the previous steps. + 1. Enter the required Slack credentials for the application you created in Slack. + 1. Optionally, provide a **Landing Page URL** that Slack users will be redirected to after they add your bot. + 1. The **OAuth & Permissions Redirect URL** and **Event Subscription Request URL** values should match the values you entered in Slack to [add the redirect URL](#add-a-new-redirect-url) and to [subscribe to bot events](#subscribe-to-bot-events). -```json - "SlackVerificationToken": "", - "SlackBotToken": "", - "SlackClientSigningSecret": "" -``` +1. Select **Apply**. +1. You're redirected to Slack to finish installing your Slack app. + - If the requested permissions look correct, select **Allow**. -### Inject the Slack adapter In your bot startup.cs +Your bot's now configured to communicate with users in Slack. +Users in the workspace can now interact with your bot via the Slack app. -Add the following line to the ***ConfigureServices*** method within your startup.cs file. This will register your Slack adapter and make it available for your new controller class. The configuration settings you added in the previous step will be automatically used by the adapter. +## Test your application in Slack -```csharp -services.AddSingleton(); -``` +1. Sign in to the Slack workspace where you installed your app. +1. Under **Apps**, select your app. +1. In the resulting pane, send messages to the application. -Once added, your ***ConfigureServices*** method shold look like this. - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - // Create the default Bot Framework Adapter (used for Azure Bot Service channels and emulator). - services.AddSingleton(); - - // Create the Slack Adapter - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -## Complete configuration of your Slack app - -### Obtain a URL for your bot - -Now that you have created a Slack app and wired up the adapter in your bot project, the final step is to point the Slack app to the correct endpoint on your bot and subscribe your app to ensure your bot receives messages. To do this your bot must be running, so that Slack can verify the URL to the endpoint is valid. - -To complete this step, [deploy your bot to Azure](https://aka.ms/bot-builder-deploy-az-cli) and make a note of the URL to your deployed bot. +## Additional information > [!NOTE] -> If you are not ready to deploy your bot to Azure, or wish to debug your bot when using the Slack adapter, you can use a tool such as [ngrok](https://www.ngrok.com) (which you will likely already have installed if you have used the Bot Framework emulator previously) to tunnel through to your bot running locally and provide you with a publicly accessible URL for this. -> -> If you wish create an ngrok tunnel and obtain a URL to your bot, use the following command in a terminal window (this assumes your local bot is running on port 3978, alter the port numbers in the command if your bot is not). -> -> ``` -> ngrok.exe http 3978 -host-header="localhost:3978" -> ``` - -### Update your Slack app +> As of June 2020 Slack channel supports Slack V2 permission scopes, which allow the bot to specify its capabilities and permissions in a more granular way. All newly configured Slack channels will use the V2 scopes. To switch your bot to the V2 scopes, delete and recreate the Slack channel configuration in the Azure portal **Channels** blade. -Navigate back to the [Slack API dashboard]([https://api.slack.com/apps]) and select your app. You now need to configure 2 URLs for your app and subscribe to the appropriate events. +For more information about Slack support for bots, see the Slack API documentation: -1. In the **OAuth & Permissions** tab, the **Redirect URL** should be your bot's URL, plus the `api/slack` endpoint you specified in your newly created controller. For example, `https://yourboturl.com/api/slack`. +- [Developer docs and guides](https://api.slack.com/docs) +- [Permission scopes](https://api.slack.com/scopes) +- [Understanding OAuth scopes for Bots](https://api.slack.com/tutorials/tracks/understanding-oauth-scopes-bot) -![Slack redirect URL](~/media/bot-service-adapter-connect-slack/redirect-url.png) - -2. In the **Event Subscriptions** tab, fill in the **Request URL** with the same URL you used in step 1. - -3. Enable events using the toggle at the top of the page. - -4. Expand the **Subscribe to bot events** section and use the **Add Bot User Event** button to subscribe to the **im_created** and **message.im** events. - -![Slack event subscriptions](~/media/bot-service-adapter-connect-slack/event-subscriptions.png) - -## Test your bot with adapter in Slack - -Your Slack app is now configured and you can now login to the Slack workspace you installed your app into. (You will see it listed under the 'Apps' section of the left hand menu.) Select your app and try sending a message. You should see it echoed back to you in the IM window. +## Connect a bot to Slack using the Slack adapter -You can also test this feature using the [sample bot for the Slack adapter](https://aka.ms/csharp-60-slack-adapter-sample) by populating the appSettings.json file with the same values described in the steps above. This sample has additional steps described in the README file to show examples of link sharing, receiving attachments, and sending interactive messages. +As well as the channel available in the Azure AI Bot Service to connect your bot with Slack, the [Bot Builder Community repos](https://github.com/BotBuilderCommunity/) define a custom channel adapter for Slack. ---- +- For information on the C# adapter, see [the Adapters section](https://github.com/BotBuilderCommunity/botbuilder-community-dotnet#adapters) in the .NET community repo. +- For information on the JavaScript adapter, see [the Adapters section](https://github.com/BotBuilderCommunity/botbuilder-community-js#adapters) in the JavaScript community repo. diff --git a/articles/bot-service-channel-connect-telegram.md b/articles/bot-service-channel-connect-telegram.md index 175e5ede0..cef04ec29 100644 --- a/articles/bot-service-channel-connect-telegram.md +++ b/articles/bot-service-channel-connect-telegram.md @@ -1,63 +1,56 @@ --- -title: Create a bot for Telegram - Bot Service -description: Learn how to configure a bot's connection to Telegram. +title: Connect a Bot Framework bot to Telegram +description: Learn how to configure your bot to use the Telegram messaging app to communicate with people. keywords: configure bot, Telegram, bot channel, Telegram bot, access token -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - abs-meta-21q1 + - evergreen --- # Connect a bot to Telegram -You can configure your bot to communicate with people using the Telegram messaging app. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -[!INCLUDE [Channel Inspector intro](~/includes/snippet-channel-inspector.md)] +You can configure your bot to communicate with people using the Telegram messaging app. This article describes how create a Telegram bot and connect it to your bot in the Azure portal. -## Visit the Bot Father to create a new Telegram bot +[!INCLUDE [Channel Inspector intro](includes/snippet-channel-inspector.md)] -Create a new Telegram bot using the Bot Father. +## Prerequisites -![Visit Bot Father](~/media/channels/tg-StepVisitBotFather.png) +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure. +- A device with [Telegram](https://telegram.org/) installed and a Telegram account. -## Create a new Telegram bot -To create a new Telegram bot, send command `/newbot`. +## Create a new Telegram bot with BotFather -![Create new bot](~/media/channels/tg-StepNewBot.png) +Create a Telegram bot with BotFather before connecting your bot to Telegram. -### Specify a friendly name +1. Start a new conversation with the [BotFather](https://telegram.me/botfather). -Give the Telegram bot a friendly name. + :::image type="content" source="media/channels/tg-StepVisitBotFather.png" alt-text="Visit Bot Father"::: -![Give bot a friendly name](~/media/channels/tg-StepNameBot.png) +1. Send `/newbot` to create a new Telegram bot. +1. When asked, enter a name for the bot. +1. Give the Telegram bot a unique username. Note that the bot name must end with the word "bot" (case-insensitive). +1. Copy and save the Telegram bot's access token for later steps. -### Specify a username +## Configure Telegram in the Azure portal -Give the Telegram bot a unique username. +Now that you have an access token, you can configure your bot in the Azure portal to communicate with Telegram. -![Give bot a unique name](~/media/channels/tg-StepUsername.png) +1. Log in to the [Azure](https://portal.azure.com) portal. +1. Go to your bot. Then select **Channels** from **Settings**. +1. Select **Telegram** from the list of **Available Channels**. +1. Enter the token you copied previously into the **Access Token** field and select **Apply**. -### Copy the access token +Your bot's now successfully configured to communicate with users in Telegram. -Copy the Telegram bot's access token. +## Additional information -![Copy access token](~/media/channels/tg-StepBotCreated.png) - -## Enter the Telegram bot's access token - -Go to your bot's **Channels** section in the Azure portal and click the **Telegram** button. - -> [!NOTE] -> The Azure portal UI will look slightly different if you have already connected your bot to Telegram. - -![Select Telegram in channels](~/media/channels/tg-connectBot-Azure.png) - -Paste the token you copied previously into the **Access Token** field and click **Save**. - -![Telegram access token](~/media/channels/tg-accessToken-Azure.png) - -Your bot is now successfully configured to communicate with users in Telegram. - -![Telegram bot enabled](~/media/channels/tg-botEnabled-Azure.png) +For information about using Telegram-specific actions in messages, see how to [Implement channel-specific functionality](v4sdk/bot-builder-channeldata.md). diff --git a/articles/bot-service-channel-connect-twilio.md b/articles/bot-service-channel-connect-twilio.md index b64561ce5..509235cb4 100644 --- a/articles/bot-service-channel-connect-twilio.md +++ b/articles/bot-service-channel-connect-twilio.md @@ -1,225 +1,88 @@ --- -title: Connect a bot to Twilio - Bot Service -description: Learn how to configure a bot's connection to Twilio. +title: "Connect a bot to Twilio (SMS)" +description: Learn how to configure bots to use SMS via Twilio to communicate with people with a TwiML application or the Twilio adapter. keywords: Twilio, bot channels, SMS, App, phone, configure Twilio, cloud communication, text -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 10/9/2018 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Connect a bot to Twilio +# Connect a bot to Twilio (SMS) -You can configure your bot to communicate with people using the Twilio cloud communication platform. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## Log in to or create a Twilio account for sending and receiving SMS messages +You can configure your bot to communicate with people using the Twilio (SMS) cloud communication platform. This article describes how to configure a bot to communicate using Twilio by creating a TwiML application and connecting the bot in the Azure portal. -If you don't have a Twilio account, create a new account. +## Prerequisites -## Create a TwiML Application +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure. -Create a TwiML application following the instructions. +## Create a TwiML application -![Create app](~/media/channels/twi-StepTwiml.png) +1. If you don't have a Twilio account, [create a new account](https://www.twilio.com/try-twilio). If you already have a Twilio account, continue to the next step. +1. Follow the instructions to [create a TwiML application](https://support.twilio.com/hc/articles/223180928-How-Do-I-Create-a-TwiML-App-). + - Enter a **Friendly Name** for your TwiML app. + - Under **Voice Configuration**, leave the **Request URL** empty + - Under **Messaging Configuration**, set the **Request URL**: + - For a global bot, enter `https://sms.botframework.com/api/sms`. + - For a regional bot, enter following url according to the selected region: -Under **Properties**, enter a **FRIENDLY NAME**. In this tutorial we use "My TwiML app" as an example. The **REQUEST URL** under Voice can be left empty. Under **Messaging**, the **Request URL** should be `https://sms.botframework.com/api/sms`. + | Region | Request URL | + |:-|:-| + | Europe| `https://europe.sms.botframework.com/api/sms` | + | India | `https://india.sms.botframework.com/api/sms` | -## Select or add a phone number +### Select or add a phone number -Follow the instructions here to add a verified caller ID via the console site. After you finish, you will see your verified number in **Active Numbers** under **Manage Numbers**. +Follow the instructions to [add a verified caller via the Console](https://support.twilio.com/hc/articles/223180048-Adding-a-Verified-Phone-Number-or-Caller-ID-with-Twilio). You can skip this if you already have a verified caller ID. -![Set phone number](~/media/channels/twi-StepPhone.png) +After you finish, you'll see your verified number in **Verified Caller IDs**. -## Specify application to use for Voice and Messaging +### Specify TwiML app to use for voice and messaging -Click the number and go to **Configure**. Under both Voice and Messaging, set **CONFIGURE WITH** to be TwiML App and set **TWIML APP** to be My TwiML app. After you finish, click **Save**. +After adding a verified caller ID, configure your number's setting to use the TwiML app you created. -![Specify application](~/media/channels/twi-StepPhone2.png) +1. Select **Active numbers** under **Phone Numbers > Manage**. Select the number and go to **Configure**. +1. Under both **Voice & Fax** and **Messaging**, set **Configure With** to ***TwiML App**. Then set **TwiML APP** to the TwiML app you created earlier. After you finish, select **Save**. +1. Select **Active Numbers** again. You'll see the **Active Configuration** of both **Voice** and **Messaging** are set to your TwiML App. -Go back to **Manage Numbers**, you will see the configuration of both Voice and Messaging are changed to TwiML App. +### Gather credentials from Twilio -![Specified number](~/media/channels/twi-StepPhone3.png) +1. Go back to the [Twilio Console homepage](https://www.twilio.com/console/) +1. Under **Account Info**, you'll see your **Account SID** and **Auth Token** on the project dashboard, shown below. Copy and save these values for later steps. + :::image type="content" source="media/channels/twi-StepAuth.png" alt-text="Gather app credentials from Twilio Console"::: -## Gather credentials +### Enter Twilio credentials in the Azure portal -Go back to the [console homepage](https://www.twilio.com/console/), you will see your Account SID and Auth Token on the project dashboard, as shown below. +Now that you have the necessary values from Twilio, connect your bot to Twilio in the Azure portal. -![Gather app credentials](~/media/channels/twi-StepAuth.png) +1. In a separate window or tab, go to the [Azure portal](https://portal.azure.com/). +1. Select the bot that you want to connect to Twilio. +1. Under **Settings**, select **Channels**, then select the **Twilio (SMS)** icon from the list of **Available Channels**. +1. Enter the **Phone Number**, **Account Sid**, and **Auth Token** you saved earlier. After you finish, select **Apply**. -## Submit credentials + :::image type="content" source="media/channels/twi-StepSubmit.png" alt-text="Enter Twilio credentials in Azure"::: -In a separate window, return to the Bot Framework site at https://dev.botframework.com/. +Your bot's now successfully configured to communicate with Twilio users. -- Select **My bots** and choose the Bot that you want to connect to Twilio. This will direct you to the Azure portal. -- Select **Channels** under **Bot Management**. Click the Twilio (SMS) icon. -- Enter the Phone Number, Account SID, and Auth Token you record earlier. After you finish, click **Save**. +## Test your bot in Twilio -![Submit credentials](~/media/channels/twi-StepSubmit.png) +To test whether your bot is connected to Twilio correctly, send an SMS message to your Twilio number. When your bot receives the message, it sends a message back to you, echoing the text from your message. -When you have completed these steps, your bot will be successfully configured to communicate with users using Twilio. +## Additional information -## Connect a bot to Twilio using the Twilio adapter +To learn more about developing for Twilio, see the [Twilio SMS documentation](https://www.twilio.com/docs/sms). -As well as the channel available in the Azure Bot Service to connect your bot with Twilio, you can also use the Twilio adapter. In this article you will learn how to connect a bot to Twilio using the adapter. This article will walk you through modifying the EchoBot sample to connect it to Twilio. +### Connect a bot to Twilio using the Twilio adapter -> [!NOTE] -> The instructions below cover the C# implementation of the Twilio adapter. For instructions on using the JS adapter, part of the BotKit libraries, [see the BotKit Twilio documentation](https://botkit.ai/docs/v4/platforms/twilio.html). +In addition to using the available Azure AI Bot Service channel to connect your bot with Twilio, the [Bot Builder Community repos](https://github.com/BotBuilderCommunity/) define a custom channel adapter for Twilio. -### Prerequisites - -* The [EchoBot sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/02.echo-bot) - -* A Twilio account. If you do not have a Twilio account, you can [create one here](https://www.twilio.com/try-twilio). - -### Get a Twilio number and gather account credentials - -1. Log into [Twilio](https://twilio.com/console). On the right hand side of the page you will see the **ACCOUNT SID** and **AUTH TOKEN** for your account, make a note of these as you will need them later when configuring your bot application. - -2. Choose **Programmable Voice** from the options under **Get Started with Twilio**. - -![Get started with Programmable Voice](~/media/bot-service-channel-connect-twilio/get-started-voice.png) - -3. On the next page, click the **Get your first Twilio number** button. A pop-up window will show you a new number, which you can accept by clicking **Choose this number** (alternatively you can search for a different number by following the on screen instructions). - -4. Once you have chosen your number, make a note of it, as you will need this when configuring your bot application in a later step. - -### Wiring up the Twilio adapter in your bot - -Now that you have your Twilio number and account credentials, you need to configure your bot application. - -#### Install the Twilio adapter NuGet package - -Add the [Microsoft.Bot.Builder.Adapters.Twilio](https://www.nuget.org/packages/Microsoft.Bot.Builder.Adapters.Twilio/) NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs). - -#### Create a Twilio adapter class - -Create a new class that inherits from the ***TwilioAdapter*** class. This class will act as our adapter for the Twilio channel and include error handling capabilities (similar to the ***BotFrameworkAdapterWithErrorHandler*** class already in the sample, used for handling other requests from Azure Bot Service). - -```csharp -public class TwilioAdapterWithErrorHandler : TwilioAdapter -{ - public TwilioAdapterWithErrorHandler(IConfiguration configuration, ILogger logger) - : base(configuration, logger) - { - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); - - // Send a message to the user - await turnContext.SendActivityAsync("The bot encountered an error or bug."); - await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); - - // Send a trace activity, which will be displayed in the Bot Framework Emulator - await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); - }; - } -} -``` - -#### Create a new controller for handling Twilio requests - -Create a new controller which will handle requests from Twilio, on a new endpoing 'api/twilio' instead of the default 'api/messages' used for requests from Azure Bot Service Channels. By adding an additional endpoint to your bot, you can accept requests from Bot Service channels, as well as from Twilio, using the same bot. - -```csharp -[Route("api/twilio")] -[ApiController] -public class TwilioController : ControllerBase -{ - private readonly TwilioAdapter _adapter; - private readonly IBot _bot; - - public TwilioController(TwilioAdapter adapter, IBot bot) - { - _adapter = adapter; - _bot = bot; - } - - [HttpPost] - [HttpGet] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _adapter.ProcessAsync(Request, Response, _bot); - } -} -``` - -#### Inject the Twilio adapter in your bot startup.cs - -Add the following line to the ***ConfigureServices*** method within your startup.cs file. This will register your Twilio adapter and make it available for your new controller class. The configuration settings you added in the previous step will be automatically used by the adapter. - -```csharp -services.AddSingleton(); -``` - -Once added, your ***ConfigureServices*** method shold look like this. - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - // Create the default Bot Framework Adapter (used for Azure Bot Service channels and emulator). - services.AddSingleton(); - - // Create the Twilio Adapter - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -#### Obtain a URL for your bot - -Now that you have wired up the adapter in your bot project, you need to identify the correct endpoint to provide to Twilio in order to ensure your bot receives messages. You also require this URL to complete configuration of your bot application. - -To complete this step, [deploy your bot to Azure](https://aka.ms/bot-builder-deploy-az-cli) and make a note of the URL of your deployed bot. - -> [!NOTE] -> If you are not ready to deploy your bot to Azure, or wish to debug your bot when using the Twilio adapter, you can use a tool such as [ngrok](https://www.ngrok.com) (which you will likely already have installed if you have used the Bot Framework emulator previously) to tunnel through to your bot running locally and provide you with a publicly accessible URL for this. -> -> If you wish create an ngrok tunnel and obtain a URL to your bot, use the following command in a terminal window (this assumes your local bot is running on port 3978, alter the port numbers in the command if your bot is not). -> -> ``` -> ngrok.exe http 3978 -host-header="localhost:3978" -> ``` - -#### Add Twilio app settings to your bot's configuration file - -Add the settings shown below to your appSettings.json file in your bot project. You populate **TwilioNumber**, **TwilioAccountSid** and **TwilioAuthToken** using the values you gathered when creating your Twilio number. **TwilioValidationUrl** should be your bot's URL, plus the `api/twilio` endpoint you specified in your newly created controller. For example, `https://yourboturl.com/api/twilio`. - - -```json - "TwilioNumber": "", - "TwilioAccountSid": "", - "TwilioAuthToken": "", - "TwilioValidationUrl", "" -``` - -Once you have populated the settings above, you should redeploy (or restart if running locally with ngrok) your bot. - -### Complete configuration of your Twilio number - -The final step is to configure your new Twilio number's messaging endpoint, to ensure your bot receives messages. - -1. Navigate to the Twilio [Active Numbers page](https://www.twilio.com/console/phone-numbers/incoming). - -2. Click the phone number you created in the earlier step. - -3. Within the **Messaging** section, complete the **A MESSAGE COMES IN** section by chooisng **Webhook** from the drop down and populating the text box with your bot's endpoint that you used to populate the **TwilioValidationUrl** setting in the previous step, such as `https://yourboturl.com/api/twilio`. - -![Configure Twilio number webhook](~/media/bot-service-channel-connect-twilio/twilio-number-messaging-settings.png) - -4. Click the **Save** button. - -### Test your bot with adapter in Twilio - -You can now test whether your bot is connected to Twilio correctly by sending an SMS message to your Twilio number. Once the message is received by your bot it will send a message back to you, echoing the text from your message. - -You can also test this feature using the [sample bot for the Twilio adapter](https://aka.ms/csharp-63-twilio-adapter-sample) by populating the appSettings.json with the same values described in the steps above. +- For information on the C# adapter, see [the Adapters section in the .NET community repo](https://github.com/BotBuilderCommunity/botbuilder-community-dotnet#adapters). +- For information on the JavaScript adapter, see [the Adapters section in the JavaScript community repo](https://github.com/BotBuilderCommunity/botbuilder-community-js#adapters). diff --git a/articles/bot-service-channel-connect-webchat-speech.md b/articles/bot-service-channel-connect-webchat-speech.md deleted file mode 100644 index e26ea1f6a..000000000 --- a/articles/bot-service-channel-connect-webchat-speech.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Enable speech in Web Chat - Bot Service -description: Learn how to enable speech in the web chat control for a bot connected to the Web Chat channel. -keywords: speech, web chat, voice, microphone, audio -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Enable speech in Web Chat -You can enable a voice interface in the Web Chat control. Users interact with the voice interface by using the microphone in the Web Chat control. - -![Web chat speech sample](~/media/bot-service-channel-webchat/webchat-sample-speech.png) - -If the user types instead of speaking a response, Web Chat turns off the speech functionality and the bot gives only a textual response instead of speaking out loud. To re-enable the spoken response, the user can use the microphone to respond to the bot the next time. If the microphone is accepting input, it appears dark or filled-in. If it's grayed out, the user clicks on it to enable it. - -## Prerequisites - - Before you run the sample, you need to have a Direct Line secret or token for the bot that you want to run using the Web Chat control. - * See [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) for information on getting a Direct Line secret associated with your bot. - * See [Generate a Direct Line token](rest-api/bot-framework-rest-direct-line-3-0-authentication.md) for information on exchanging the secret for a token. - -## Customizing Web Chat for speech -To enable the speech functionality in Web Chat, you need to customize the JavaScript code that invokes the Web Chat control. You can try out voice-enabled Web Chat locally using the following steps. - -1. Download the [sample index.html](https://aka.ms/web-chat-speech-sample). -2. Edit the code in `index.html` according to the type of speech support you want to add. The types of speech implementations are described in [Enable speech services](#enable-speech-services). -3. Start a web server. One way to do so is to use `npm http-server` at a Node.js command prompt. - - * To install `http-server` globally so it can be run from the command line, run this command: - - ``` - npm install http-server -g - ``` - - * To start a web server using port 8000, from the directory that contains `index.html`, run this command: - - ``` - http-server -p 8000 - ``` -4. Aim your browser at `http://localhost:8000/samples?parameters`. For example, `http://localhost:8000/samples?s=YOURDIRECTLINESECRET` invokes the bot using a Direct Line secret. The parameters that can be set in the query string are described in the following table: - - | Parameter | Description | - |-----------|-------------| - | s | Direct Line secret. See [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) for information on getting a Direct Line secret. | - | t | Direct Line token. See [Generate a Direct Line token](rest-api/bot-framework-rest-direct-line-3-0-authentication.md) for info on how to generate this token. | - | domain | Optional. The URL of an alternate Direct Line endpoint. | - | webSocket | Optional. Set to 'true' to use WebSocket to receive messages. Default is `false`. | - | userid | Optional. The ID of the bot user. | - | username | Optional. The user name of the bot's user. | - | botid | Optional. ID of the bot. | - | botname | Optional. Name of the bot. | - - -## Enable speech services -The customization allows you to add speech functionality in any of the following ways: - -* **Browser-provided speech** - Use speech functionality built into the web browser. At this time, this functionality is only available on the Chrome browser. - -* **Create a custom speech service** - You can create your own custom speech recognition and voice synthesis components. - -### Browser-provided speech - -The following code instantiates speech recognizer and speech synthesis components that come with the browser. This method of adding speech is not supported by all browsers. - -> [!NOTE] -> Google Chrome supports the browser speech recognizer. However, Chrome may block the microphone in the following cases: -> * If the URL of the page that contains Web Chat begins with `http://` instead of `https://`. -> * If the URL is a local file using the `file://` protocol instead of `http://localhost:8000`. - -[!code-js[Specify speech options to use in-browser speech (JavaScript)](./includes/code/bot-service-channel-connect-webchat-speech.js#BrowserSpeech)] - - -### Custom Speech service - -You can also provide your own custom speech recognition that implements ISpeechRecognizer or speech synthesis that implements ISpeechSynthesis. - -[!code-js[Fetch a token to use with a custom speech service (JavaScript)](./includes/code/bot-service-channel-connect-webchat-speech.js#CustomSpeechService)] - -### Pass the speech options to Web Chat - -The following code passes the speech options to the Web Chat control: - -[!code-js[Pass speech options to Web Chat (JavaScript)](./includes/code/bot-service-channel-connect-webchat-speech.js#PassSpeechOptionsToWebChat)] - -## Next steps -Now that you can enable voice interaction with Web Chat, learn how your bot constructs spoken messages and adjusts the state of the microphone: -* [Add speech to messages (C#)](dotnet/bot-builder-dotnet-text-to-speech.md) -* [Add speech to messages (Node.js)](nodejs/bot-builder-nodejs-text-to-speech.md) - -## Additional resources - -* You can [download the source code](https://github.com/Microsoft/BotFramework-WebChat) for the web chat control on GitHub. -* The [Bing Speech API documentation](https://docs.microsoft.com/azure/cognitive-services/speech/home) provides more information on the Bing Speech API. - diff --git a/articles/bot-service-channel-connect-webchat.md b/articles/bot-service-channel-connect-webchat.md index 22687385e..115691808 100644 --- a/articles/bot-service-channel-connect-webchat.md +++ b/articles/bot-service-channel-connect-webchat.md @@ -1,86 +1,115 @@ --- -title: Connect a bot to the Web Chat channel - Bot Service -description: Learn how to use the web chat control in your web page for a bot connected to the Web Chat channel. -keywords: web chat, bot channel, web page, secret key, iframe, HTML -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 08/22/2019 +title: Connect a bot to Web Chat in the Bot Framework SDK +description: Learn how to use the Web Chat control to connect to a bot that uses the Web Chat channel. +keywords: web chat, bot channel, web page, secret key, HTML +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to Web Chat -[!INCLUDE [pre-release-label](./includes/pre-release-label.md)] +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -When you [create a bot](bot-service-quickstart.md) with Bot Service, the Web Chat channel is automatically configured for you. The Web Chat channel includes the web chat control, which provides the ability for users to interact with your bot directly in a web page. +When you [create a bot](bot-service-quickstart.md) with Azure, the Web Chat channel is automatically configured for you. The Web Chat channel includes the [Web Chat control](https://github.com/microsoft/BotFramework-WebChat), which provides the ability for users to interact with the bot directly in a web page. -![Web chat sample](./media/bot-service-channel-webchat/create-a-bot.png) +The Web Chat channel contains everything you need to embed the Web Chat control in a web page. To do so, you'll get your bot's secret key and then embed the control in a web page. -The Web Chat channel in the Bot Framework Portal contains everything you need to embed the web chat control in a web page. All you have to do to use the web chat control is get your bot's secret key and embed the control in a web page. +## Prerequisites -## Web Chat and Direct Line considerations +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- An existing bot published to Azure. -> [!IMPORTANT] -> Please, keep in mind these important [Security considerations](rest-api/bot-framework-rest-direct-line-3-0-authentication.md#security-considerations). +## Web Chat security considerations + +When you use Azure AI Bot Service authentication with Web Chat, there are some important security considerations you must keep in mind. For more information, see [Security considerations](rest-api/bot-framework-rest-direct-line-3-0-authentication.md#security-considerations). -## Get your bot secret key +## Embed the Web Chat control in a web page -1. Open your bot in the [Azure Portal](https://portal.azure.com) and click **Channels** blade. +The following image shows the components involved when embedding the Web Chat control in a web page. -2. Click **Edit** for the **Web Chat** channel. -![Web chat channel](./media/bot-service-channel-webchat/bot-service-channel-list.png) +:::image type="content" source="./media/bot-service-channel-webchat/webchat-control.png" alt-text="Web Chat control components"::: + +> [!IMPORTANT] +> Use Direct Line (with enhanced authentication) to mitigate security risks when connecting to a bot using the Web Chat control. For more information, see [Direct Line enhanced authentication](v4sdk\bot-builder-security-enhanced.md). -3. Under **Secret keys**, click **Show** for the first key. -![Secret key](./media/bot-service-channel-webchat/secret-key.png) +### Get your bot secret key -4. Copy the **Secret key** and the **Embed code**. +1. Go to [Azure portal](https://portal.azure.com) and open your bot. +1. Under **Settings**, select **Channels**. Then select **Web Chat**. +1. The **Web Chat** page will open. Select the **Default Site** from the list of **Sites**. +1. Copy the first **Secret key** and the **Embed code**. -5. Click **Done**. +### Development embedding options -## Embed the web chat control in your website +#### Option 1 - Exchange your secret for a token, and generate the embed -You can embed the web chat control in your website by using one of two options. +This is a good option if: -### Option 1 - Keep your secret hidden, exchange your secret for a token, and generate the embed +- you can execute a server-to-server request to exchange your web chat secret for a temporary token +- you want to make it difficult for other developers to embed your bot in their websites -Use this option if you can execute a server-to-server request to exchange your web chat secret for a temporary token, -and if you want to make it difficult for other developers to embed your bot in their websites. -Although using this option will not absolutely prevent other developers from embedding your bot in their websites, -it does make it difficult for them to do so. +Using this option won't completely prevent other developers from embedding your bot in their websites, but it does make it difficult for them to do so. To exchange your secret for a token and generate the embed: -1. Issue a **GET** request to `https://webchat.botframework.com/api/tokens` and pass your web chat secret via the `Authorization` header. The `Authorization` header uses the `BotConnector` scheme and includes your secret, as shown in the example request below. +1. Issue a **GET** request to the token exchange URL and pass your web chat secret via the `Authorization` header. The `Authorization` header uses the `BotConnector` scheme and includes your secret. + - For a global bot, the token exchange URL is `https://webchat.botframework.com/api/tokens`. + - For a regional bot, enter following url according to the selected region: -2. The response to your **GET** request will contain the token (surrounded with quotation marks) that can be used to start a conversation by rendering the web chat control within an **iframe**. A token is valid for one conversation only; to start another conversation, you must generate a new token. + | Region | Token Exchange URL | + |:-|:-| + | Europe| https://europe.webchat.botframework.com/api/tokens | + | India | https://india.webchat.botframework.com/api/tokens | -3. Within the `iframe` **Embed code** that you copied from the Web Chat channel within the Bot Framework Portal (as described in [Get your bot secret key](#get-your-bot-secret-key) above), change the `s=` parameter to `t=` and replace "YOUR_SECRET_HERE" with your token. +1. The response to your **GET** request will contain the token (surrounded with quotation marks) that can be used to start a conversation by rendering the Web Chat control. A token is valid for one conversation only; to start another conversation, you need to generate a new token. + +1. Within the **Embedded code** that you copied from the Web Chat channel earlier, change the `s=` parameter to `t=` and replace "YOUR_SECRET_HERE" with your token. > [!NOTE] -> Tokens will automatically be renewed before they expire. +> Tokens will automatically be renewed before they expire. -##### Example request +##### Example requests -``` +For a global bot: + +```http requestGET https://webchat.botframework.com/api/tokens Authorization: BotConnector YOUR_SECRET_HERE ``` -##### Example response +For a regional bot: -```response -"IIbSpLnn8sA.dBB.MQBhAFMAZwBXAHoANgBQAGcAZABKAEcAMwB2ADQASABjAFMAegBuAHYANwA.bbguxyOv0gE.cccJjH-TFDs.ruXQyivVZIcgvosGaFs_4jRj1AyPnDt1wk1HMBb5Fuw" +```http +requestGET + ## Europe region + https://europe.webchat.botframework.com/api/tokens + ## India region + https://india.webchat.botframework.com/api/tokens +Authorization: BotConnector YOUR_SECRET_HERE ``` -##### Example iframe (using token) +> [!NOTE] +> For Azure Government, the token exchange URL is different. -```html - +```http +requestGET https://webchat.botframework.azure.us/api/tokens +Authorization: BotConnector YOUR_SECRET_HERE ``` -##### Example html code +##### Example response + +```response +"IIbSpLnn8sA.dBB.MQBhAFMAZwBXAHoANgBQAGcAZABKAEcAMwB2ADQASABjAFMAegBuAHYANwA.bbguxyOv0gE.cccJjH-TFDs.ruXQyivVZIcgvosGaFs_4jRj1AyPnDt1wk1HMBb5Fuw" +``` + +##### Example HTML code + ```html @@ -98,7 +127,7 @@ Authorization: BotConnector YOUR_SECRET_HERE function processRequest(e) { if (xhr.readyState == 4 && xhr.status == 200) { var response = JSON.parse(xhr.responseText); - document.getElementById("chat").src="https://webchat.botframework.com/embed/lucas-direct-line?t="+response + document.getElementById("chat").src="https://webchat.botframework.com/embed/?t="+response } } @@ -106,36 +135,94 @@ Authorization: BotConnector YOUR_SECRET_HERE ``` -### Option 2 - Embed the web chat control in your website using the secret + + +#### Option 2 - Embed the web chat control in your website using the secret -Use this option if you want to allow other developers to easily embed your bot into their websites. +Use this option if you want to allow other developers to easily embed your bot into their websites. > [!WARNING] -> If you use this option, other developers can embed your bot into their websites -> by simply copying your embed code. +> With this option, the Web Chat channel secret key is exposed in the client web page. Use this option only for development purposes and not in a production environment. -To embed your bot in your website by specifying the secret within the `iframe` tag: +To embed your bot in a web page by specifying the secret within the **Embedded code**: -1. Copy the `iframe` **Embed code** from the Web Chat channel within the Bot Framework Portal (as described in [Get your bot secret key](#get-your-bot-secret-key) above). +1. Copy the **Embedded code** from the Web Chat channel within the Azure portal (described in [Get your bot secret key](#get-your-bot-secret-key) above). -2. Within that **Embed code**, replace "YOUR_SECRET_HERE" with the **Secret key** value that you copied from the same page. +1. Within that **Embedded code**, replace "YOUR_SECRET_HERE" with the **Secret key** value that you copied from the same page. -##### Example iframe (using secret) +### Production embedding option -```html - -``` +#### Keep your secret hidden, exchange your secret for a token, and generate the embed + +This option doesn't expose the Web Chat channel secret key in the client web page. -## Style the web chat control +The client needs to provide a token to talk to the bot. To learn about the differences between secrets and tokens +and to understand the risks associated with using secrets, see [Direct Line authentication](rest-api/bot-framework-rest-direct-line-3-0-authentication.md). -You may change the size of the web chat control by using the `style` attribute of the `iframe` to specify `height` and `width`. +The following client web page shows how to use a token with the Web Chat. If you have a regional or Azure Gov bot, adjust the URLs from public to government. ```html - + + + + + + +

Web Chat bot client using Direct Line

+ +
+ + + + ``` -![Chat control Client](./media/chatwidget-client.png) +For examples on how to generate a token, see: + +- [Single sign-on demo](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/07.advanced-web-chat-apps/e.sso-on-behalf-of-authentication) +- [Direct Line token](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/k.direct-line-token) -## Additional resources +## Additional information -You can [download the source code](https://aka.ms/BotFramework-WebChat-V4) for the web chat control on GitHub. +- [Web Chat overview](./v4sdk/bot-builder-webchat-overview.md) +- [Web Chat customization](./v4sdk/bot-builder-webchat-customization.md) +- [Enable speech in Web Chat](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/03.speech/a.direct-line-speech) +- [Use Web Chat with the Direct Line App Service Extension](./bot-service-channel-directline-extension-webchat-client.md) +- [Add single sign-on to Web Chat](./v4sdk/bot-builder-webchat-sso.md) diff --git a/articles/bot-service-channel-connect-wechat.md b/articles/bot-service-channel-connect-wechat.md index 20bf79e95..37873ca87 100644 --- a/articles/bot-service-channel-connect-wechat.md +++ b/articles/bot-service-channel-connect-wechat.md @@ -2,160 +2,23 @@ title: Connect a bot to WeChat - Bot Service description: Learn how to configure a bot's connection to WeChat. keywords: WeChat, Tencent, bot channel, WeChat App, WeChat bot, App ID, App Secret, credentials -author: seaen -manager: kamrani -ms.topic: article -ms.author: egorn -ms.service: bot-service -ms.date: 11/01/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Connect a bot to WeChat -You can configure your bot to communicate with people using the WeChat Official Accounts Platform. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## Download WeChat Adapter for Bot Framework +You can use the WeChat custom channel adapter to configure your bot to communicate with people using the WeChat Official Accounts Platform. -WeChat adapter for Microsoft Bot Framework is an open source adapter on GitHub. [Download WeChat Adapter for Bot Framework](https://github.com/microsoft/BotFramework-WeChat/). +The WeChat adapter for Microsoft Bot Framework is an open source adapter on GitHub. -## Create a WeChat Account - -To configure a bot to communicate using WeChat, you need to create a WeChat official account on [WeChat Official Account Platform](https://mp.weixin.qq.com/?lang=en_US) and then connect the bot to the app. Currently we only support Service Account. - -### Change Your Prefer Language - -You can change the display language you prefer before login. - - ![change_language](./media/channels/wechat-change-language.png) - -### Register A Service Account - -A real service account must be verified by WeChat, you can’t enable webhook before account is verified. To create your own service account, please follow the instruction [Here](https://kf.qq.com/product/weixinmp.html#hid=87). -For short, just click the Register Now on the top, select the Service Account and follow the instruction. - - ![register_account](./media/channels/wechat-register-account.png) - -### Sandbox Account - -If you just want to test the WeChat and bot integration, you can use a sandbox account instead of creating the service account. Learn more about creating a [sandbox account](https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login). - -## Enable WeChat Adapter To Bot - -The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch it, you need to make sure you can run the bot. Download [WeChat Adapter for Bot Framework](https://github.com/microsoft/BotFramework-WeChat/). - -### Prerequisites - - .NET Core SDK (version 2.2.x) - -### Add Reference To WeChat Adapter Source - -Please directly reference the WeChat adapter project or add ~/BotFramework-WeChat/libraries/csharp_dotnetcore/outputpackages as local NuGet source. - -### Inject WeChat Adapter In Your Bot Startup.cs - -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - - // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.) - services.AddSingleton(); - - // Create the User state. (Used in this bot's Dialog implementation.) - services.AddSingleton(); - - // Create the Conversation state. (Used by the Dialog system itself.) - services.AddSingleton(); - - // Load WeChat settings. - var wechatSettings = new WeChatSettings(); - Configuration.Bind("WeChatSettings", wechatSettings); - services.AddSingleton(wechatSettings); - - // Configure hosted serivce. - services.AddSingleton(); - services.AddHostedService(); - services.AddSingleton(); - - // The Dialog that will be run by the bot. - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -### Update Your Bot Controller - -```csharp -[Route("api/messages")] -[ApiController] -public class BotController : ControllerBase -{ - private readonly IBot _bot; - private readonly WeChatHttpAdapter _weChatHttpAdapter; - private readonly string Token; - public BotController(IBot bot, WeChatHttpAdapter weChatAdapter) - { - _bot = bot; - _weChatHttpAdapter = weChatAdapter; - } - - [HttpPost("/WeChat")] - [HttpGet("/WeChat")] - public async Task PostWeChatAsync([FromQuery] SecretInfo secretInfo) - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await _weChatHttpAdapter.ProcessAsync(Request, Response, _bot, secretInfo); - } -} -``` - -### Setup appsettings.json - -You will need to set up appsettings.json before start up the bot, you can find what you need below. - -```json -"WeChatSettings": { - "UploadTemporaryMedia": true, - "PassiveResponseMode": false, - "Token": "", - "EncodingAESKey": "", - "AppId": "", - "AppSecret": "" -} -``` - -#### Service Account - -If you already have a service account and ready to deploy, then you can find **AppID** , **AppSecret** , **EncodingAESKey** and **Token** in the basic configurations on the left nav bar, like below. - -Don't forgot you need to set up the IP white list, otherwise WeChat won't accept your request. - - ![serviceaccount_console](./media/channels/wechat-serviceaccount-console.png) - -#### Sandbox Account - -Sandbox account don't have **EncodingAESKey** , message from WeChat was not encrypted just leave EncodingAESKey blank. You only have three parameters here, **appID** , **appsecret** and **Token**. - - ![sandbox_account](./media/channels/wechat-sandbox-account.png) - -### Start Bot And Set Endpoint URL - -Now you can set your bot backend. Before you are doing this, you have to start the bot before you save the settings, WeChat will send you a request to verify the URL. -Please set the endpoint in such pattern: **https://your_end_point/WeChat**, or set your personal settings the same with what you have done in BotController.cs - - ![sandbox_account2](./media/channels/wechat-sandbox-account-2.png) - -### Subscribe Your Official Account - -You can find a QR code to subscribe your test account as in WeChat. - - ![subscribe](./media/channels/wechat-subscribe.png) - -## Test Through WeChat - -Everything is done, you can try it in your WeChat client. You can try our sample bot under tests folder. This sample bot includes wechat adapter and integrated with echo bot and Cards bot. - - ![chat](./media/channels/wechat-chat.png) +1. Clone or download the [BotFramework-WeChat](https://github.com/microsoft/BotFramework-WeChat/) repo. +1. Then follow the [WeChat adapter documentation](https://github.com/microsoft/BotFramework-WeChat/tree/master/doc#readme). diff --git a/articles/bot-service-channel-directline-extension-net-bot.md b/articles/bot-service-channel-directline-extension-net-bot.md index bb05343b5..61767e7a8 100644 --- a/articles/bot-service-channel-directline-extension-net-bot.md +++ b/articles/bot-service-channel-directline-extension-net-bot.md @@ -1,108 +1,111 @@ --- -title: .NET bot with direct line app service extension -titleSuffix: Bot Service -description: Enable .NET bot to work with direct line app service extension -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: kamrani -ms.date: 01/16/2020 +title: Configure .NET bots for the Direct Line App Service extension in the Bot Framework SDK +description: Configure .NET bots to work with named pipes. Enable the Direct Line App Service extension and configure bots to use the extension. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms-custom: abs-meta-21q1 +ms.custom: + - evergreen --- # Configure .NET bot for extension -[!INCLUDE[applies-to-v4](includes/applies-to.md)] +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** -This article describes how to update a bot to work with **named pipes**, and how to enable the direct line app service extension in the **Azure App Service** resource where the bot is hosted. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article describes how to update a .NET bot to work with named pipes and how to enable the Direct Line App Service extension in the Azure App Service resource in which you deployed your bot. ## Prerequisites -In order to perform the steps described next, you must have **Azure App Service** resource and related **App Service** in Azure. +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A .NET bot deployed in Azure. +- Bot Framework SDK for .NET, 4.14.1 or later. -## Enable Direct Line App Service Extension +## Enable Direct Line App Service extension -This section describes how to enable the direct line app service extension using keys from your bot’s channel configuration and the **Azure App Service** resource where your bot is hosted. +This section describes how to enable the Direct Line App Service extension using the App Service extension key from your bot's Direct Line channel configuration. -## Update .NET Bot to use Direct Line App Service Extension +### Update bot code > [!NOTE] -> `Microsoft.Bot.Builder.StreamingExtensions` are preview packages and will not be updated. The SDK v4.7 contains the [streaming code](https://github.com/microsoft/botbuilder-dotnet/tree/master/libraries/Microsoft.Bot.Builder/Streaming) and you do not need to install the Streaming Packages separately. +> The **Microsoft.Bot.Builder.StreamingExtensions** NuGet preview packages have been deprecated. Starting with v4.8, the SDK contains a `Microsoft.Bot.Builder.Streaming` namespace. If a bot previously made use of the preview packages, they must be removed before following the steps below. 1. In Visual Studio, open your bot project. -2. Add the **Streaming Extension NuGet** package to your project: - 1. In your project, right click on **Dependencies** and select **Manage NuGet Packages**. - 2. Under the *Browse* tab, click **Include prerelease** to show the preview packages. - 3. Select the package **Microsoft.Bot.Builder.StreamingExtensions**. - 4. Click the **Install** button to install the package; read and agree to the license agreement. -3. Allow your app to use the **Bot Framework NamedPipe**: - - Open the `Startup.cs` file. - - In the ``Configure`` method, add code to ``UseBotFrameworkNamedPipe`` +1. Allow your app to use named pipes: + 1. Open the **Startup.cs** file. + 1. Add a reference to the **Microsoft.Bot.Builder.Integration.AspNet.Core** NuGet package. - ```csharp + ```csharp + using Microsoft.Bot.Builder.Integration.AspNet.Core; + ``` - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else + 1. In the `Configure` method, add a call to the `UseNamedPipes` method. + + ```csharp + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - app.UseHsts(); + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseDefaultFiles() + .UseStaticFiles() + .UseWebSockets() + // Allow the bot to use named pipes. + .UseNamedPipes(System.Environment.GetEnvironmentVariable("APPSETTING_WEBSITE_SITE_NAME") + ".directline") + .UseRouting() + .UseAuthorization() + .UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + // app.UseHttpsRedirection(); } + ``` - app.UseDefaultFiles(); - app.UseStaticFiles(); - - // Allow the bot to use named pipes. - app.UseNamedPipes(); - - app.UseMvc(); - } - ``` - -4. Save the `Startup.cs` file. -5. Open the `appsettings.json` file and enter the following values: - 1. `"MicrosoftAppId": ""` - 2. `"MicrosoftAppPassword": ""` - - The values are the **appid** and the **appSecret** associated with the service registration group. + 1. Save the **Startup.cs** file. +1. Deploy your updated bot to Azure. -6. **Publish** the bot to your Azure App Service. +### Enable bot Direct Line App Service extension -### Gather your Direct Line Extension keys +[!INCLUDE [enable Bot Direct Line App Service extensions](includes/directline-enable-dl-asp.md)] -1. In your browser, navigate to the [Azure portal](https://portal.azure.com/) -1. In the Azure portal, locate your **Azure Bot Service** resource -1. Click on **Channels** to configure the bot’s channels -1. If it is not already enabled, click on the **Direct Line** channel to enable it. -1. If it is already enabled, in the Connect to channels table click on the **Edit** link on the Direct Line row. -1. Scroll down to the App Service Extension Keys section. -1. Click on the **Show link** to reveal one of the keys, then copy its value. +## Confirm the Direct Line extension and the bot are configured -![App service extension keys](./media/channels/direct-line-extension-extension-keys.png) +[!INCLUDE [Confirm the extension and the bot are configured](includes/directline-confirm-extension-bot-config.md)] -### Enable the Direct Line App Service Extension +## Troubleshooting -1. In your browser, navigate to the [Azure portal](https://portal.azure.com/) -1. In the Azure portal, locate the **Azure App Service** resource page for the Web App where your bot is or will be hosted -1. Click on **Configuration**. Under the *Application settings* section, add the following new settings: +[!INCLUDE [Troubleshoot Direct Line extension](includes/directline-troubleshoot.md)] - |Name|Value| - |---|---| - |DirectLineExtensionKey|| - |DIRECTLINE_EXTENSION_VERSION|latest| +- Enable the bot to use the out of process hosting model; otherwise, you'll receive an *HTTP Error 500.34 - ANCM Mixed Hosting* error (where *ANCM* stands for *ASP.NET Core Module*). This error occurs because the bot template is using the `InProcess` hosting model by default. To configure out of process hosting, see [Out-of-process hosting model](/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-3.1&preserve-view=true#out-of-process-hosting-model). +For more information, see [Attributes of the aspNetCore element](/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-3.1&preserve-view=true#attributes-of-the-aspnetcore-element) and [Configuration with web.config](/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-3.1&preserve-view=true#configuration-with-webconfig). -1. Within the *Configuration* section, click on the **General** settings section and turn on **Web sockets** -1. Click on **Save** to save the settings. This restarts the Azure App Service. +- If you attempt to use OAuth with the Direct Line App Service extension and encounter the error "Unable to get the bot AppId from the audience claim", set `ClaimsIdentity` to `AudienceClaim` on the `BotFrameworkHttpAdapter`. To do so, you can subclass the adapter. For example: -## Confirm Direct Line App Extension and the Bot are Initialized + ```csharp + public class AdapterWithStaticClaimsIdentity : BotFrameworkHttpAdapter + { + public AdapterWithStaticClaimsIdentity(IConfiguration configuration, ILogger logger, ConversationState conversationState = null) + : base(configuration, logger) + { + // Manually create the ClaimsIdentity and create a Claim with a valid AudienceClaim and the AppID for a bot using the Direct Line App Service extension. + var appId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value; + ClaimsIdentity = new ClaimsIdentity(new List{ + new Claim(AuthenticationConstants.AudienceClaim, appId) + }); + } + } + ``` -In your browser, navigate to https://.azurewebsites.net/.bot. -If everything is correct, the page will return this JSON content: `{"k":true,"ib":true,"ob":true,"initialized":true}`. This is the information you obtain when **everything works correctly**, where +## Next steps -- **k** determines whether Direct Line App Service Extension (ASE) can read an App Service Extension Key from its configuration. -- **initialized** determines whether Direct Line ASE can use the App Service Extension Key to download the bot metadata from Azure Bot Service -- **ib** determines whether Direct Line ASE can establish an inbound connection with the bot. -- **ob** determines whether Direct Line ASE can establish an outbound connection with the bot. +> [!div class="nextstepaction"] +> [Use Web Chat with the Direct Line App Service extension](./bot-service-channel-directline-extension-webchat-client.md) diff --git a/articles/bot-service-channel-directline-extension-net-client.md b/articles/bot-service-channel-directline-extension-net-client.md index 0cdbd4203..82a482659 100644 --- a/articles/bot-service-channel-directline-extension-net-client.md +++ b/articles/bot-service-channel-directline-extension-net-client.md @@ -1,64 +1,56 @@ --- -title: Create .NET client for direct line app service extension -titleSuffix: Bot Service -description: .NET client C# to connect to direct line app service extension -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: kamrani -ms.date: 07/25/2019 +title: Create .NET client for Direct Line App Service extension +description: Learn how to create .NET clients that connect to Direct Line App Service extension and communicate with bots over WebSockets. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Create .NET Client to Connect to Direct Line App Service Extension +# Create .NET Client to Connect to Direct Line App Service extension -This article describes how to create a .NET client in C# which connects to the direct line app service extension. +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** -## Gather your Direct Line Extension keys +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -1. In your browser, navigate to the [Azure portal](https://portal.azure.com/) -1. In the Azure portal, locate your **Azure Bot Service** resource -1. Click on **Channels** to configure the bot’s channels -1. If it is not already enabled, click on the **Direct Line** channel to enable it. -1. If it is already enabled, in the Connect to channels table click on the **Edit** link on the Direct Line row. -1. Scroll to the Sites section. There is typically a Default Site unless you have deleted or renamed it. -1. Click on the **Show link** to reveal one of the keys, then copy its value. +This article describes how to create a .NET client in C# which connects to the Direct Line App Service extension. +Please, also read this companion article [Configure .NET bot for extension](bot-service-channel-directline-extension-net-bot.md). - ![App service extension keys](./media/channels/direct-line-extension-extension-keys-net-client.png) +## Prerequisites -> [!NOTE] -> This value is your direct line client secret used to connect to direct -line app service extension. You can create additional sites if you’d like and use -those secret values as well. +- An Azure account. +- A bot deployed to the [Azure](https://portal.azure.com) portal. + +## Get the Direct Line secret key -## Add the Preview Nuget Package Source +1. In your browser, go to the [Azure portal](https://portal.azure.com/). +1. In the Azure portal, locate your **Azure Bot** resource. +1. Select **Channels** under **Settings**. +1. If it isn't already enabled, select the **Direct Line** channel to enable it. +1. Select **Direct Line** from **Channels** after enabling it. +1. Go to the **Sites** section. There is typically a **Default_Site** unless you've deleted or renamed it. +1. Select the **Show link** button (eye icon) to reveal one of the keys; then copy and save its value. You'll use this value in the section [Create a C# Direct Line client](#create-a-c-direct-line-client). -The preview NuGet packages needed to create a C# Direct line client can be found in a NuGet feed. -1. In Visual Studio navigate to the **Tools->Options** menu item. -1. Select the **NuGet Package Manager->Package Sources** item. -1. Click on the + button to add a new package source with these values: - - Name: DL ASE Preview - - Source: https://botbuilder.myget.org/F/experimental/api/v3/index.json -1. Click on the **Update** button to save the values. -1. Click **OK** to exit the Package Sources configuration. +> [!NOTE] +> This value is your Direct Line client secret used to connect to Direct Line App Service extension. +> You can create additional sites if you'd like and use those secret values as well. -## Create a C# Direct Line Client +## Create a C# Direct Line client -Interactions with the direct line app service extension happen differently than traditional Direct Line becuase most communication happens over a *WebSocket*. The updated direct line client includes helper classes for opening and closing a *WebSocket*, sending commands through the WebSocket, and receiving Activities back from the bot. This section describes how to create a simple C# client to interact with a bot. +Interactions with the Direct Line App Service extension happen differently than traditional Direct Line because most communication happens over a *WebSocket*. The updated Direct Line client includes helper classes for opening and closing a *WebSocket*, sending commands through the WebSocket, and receiving Activities back from the bot. This section describes how to create a simple C# client to interact with a bot. -1. Create a new .NET Core 2.2 console application project in Visual Studio. -1. Add the **DirectLine client NuGet** to your project - - Click on Dependencies in the Solution tree - - Select **Manage Nuget Packages...** - - Change the Package source to the one you defined from above (DL ASE Preview) - - Find the package *Microsoft.Bot.Connector.Directline* version v3.0.3-Preview1 or later. - - Click on **Install Package**. -1. Create a client and generate a token using a secret. This step is the same as building any other C# Direct Line client except the endpoint you need use in your bot,appended with the **.bot/** path as shown next. Do not forget the ending **/**. +1. In Visual Studio, create a new .NET Core console application project. +1. Clone the [Direct Line client](https://github.com/microsoft/BotFramework-DirectLine-DotNet) from GitHub repository and include it in your project. +1. Create a client and generate a token using a secret. This step is the same as building any other C# Direct Line client except the endpoint you need use in your bot, appended with the **.bot/** path as shown next. Do not forget the ending **/**. ```csharp - string endpoint = "https://.azurewebsites.net/.bot/"; - string secret = ""; + string endpoint = "https://.azurewebsites.net/.bot/"; + string secret = ""; var tokenClient = new DirectLineClient( new Uri(endpoint), @@ -66,6 +58,10 @@ Interactions with the direct line app service extension happen differently than var conversation = await tokenClient.Tokens.GenerateTokenForNewConversationAsync(); ``` + Notice the following: + - The endpoint value is the bot URL you obtained when you deployed the bot to Azure. For more information, see [Configure .NET bot for extension](bot-service-channel-directline-extension-net-bot.md). + - The secret value shown as *YOUR_BOT_SECRET* is the value you saved earlier from the *sites section*. + 1. Once you have a conversation reference from generating a token, you can use this conversation ID to open a WebSocket with the new `StreamingConversations` property on the `DirectLineClient`. To do this you need to create a callback that will be invoked when the bot wants to send `ActivitySets` to the client: ```csharp @@ -84,7 +80,7 @@ Interactions with the direct line app service extension happen differently than } ``` -1. Now you are ready to open the WebSocket on the `StreamingConversations` property using the conversation’s token, `conversationId`, and your `ReceiveActivities` callback: +1. Now you're ready to open the WebSocket on the `StreamingConversations` property using the conversation's token, `conversationId`, and your `ReceiveActivities` callback: ```csharp var client = new DirectLineClient( diff --git a/articles/bot-service-channel-directline-extension-node-bot.md b/articles/bot-service-channel-directline-extension-node-bot.md index 8120a5789..d078a831d 100644 --- a/articles/bot-service-channel-directline-extension-node-bot.md +++ b/articles/bot-service-channel-directline-extension-node-bot.md @@ -1,101 +1,109 @@ --- -title: Node.js bot with direct line app service extension -titleSuffix: Bot Service -description: Enable Node.js bot to work with direct line app service extension -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: dev -ms.date: 01/15/2020 +title: Configure Node.js bots for Direct Line App Service extension in the Bot Framework SDK +description: Configure Node.js bots to work with named pipes. Enable the Direct Line App Service extension and configure bots to use the extension. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Configure Node.js bot for extension -[!INCLUDE[applies-to-v4](includes/applies-to.md)] +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** -This article describes how to update a bot to work with **named pipes**, and how to enable the direct line app service extension in the **Azure App Service** resource where the bot is hosted. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article describes how to update a Node.js bot to work with named pipes and how to enable the Direct Line App Service extension in the Azure App Service resource where the bot is hosted. ## Prerequisites -In order to perform the steps described next, you must have **Azure App Service** resource and related **App Service** in Azure. - -## Enable Direct Line App Service Extension - -This section describes how to enable the direct line app service extension using keys from your bot’s channel configuration and the **Azure App Service** resource where your bot is hosted. - -## Update Node.js Bot to use Direct Line App Service Extension - -1. BotBuilder v4.7.0 or higher is required to use a Node.js bot with Direct Line App Service Extension. -1. To update an existing bot using v4.x of the SDK - 1. In the root directory of your bot run `npm install --save botbuilder` to update to the latest packages. -1. Allow your app to use the **Direct Line App Service Extension Named Pipe**: - - Update the bot's index.js (below the assignment of the adapter and bot) to include: - - ```Node.js - - adapter.useNamedPipe(async (context) => { - await myBot.run(context); - }); - ``` - -1. Save the `index.js` file. -1. Update the default `Web.Config` file to add the `AspNetCore` handler needed by Direct Line App Service Extension to service requests: - - Locate the `Web.Config` file in the `wwwroot` directory of your bot and replace the default contents with: - - ```XML - - - - - - - - - - - ``` - -1. Open the `appsettings.json` file and enter the following values: - 1. `"MicrosoftAppId": ""` - 1. `"MicrosoftAppPassword": ""` - - The values are the **appid** and the **appSecret** associated with the service registration group. - -1. **Publish** the bot to your Azure App Service. - -### Gather your Direct Line App Service Extension keys - -1. In your browser, navigate to the [Azure portal](https://portal.azure.com/) -1. In the Azure portal, locate your **Azure Bot Service** resource -1. Click on **Channels** to configure the bot’s channels -1. If it is not already enabled, click on the **Direct Line** channel to enable it. -1. If it is already enabled, in the Connect to channels table click on the **Edit** link on the Direct Line row. -1. Scroll down to the **App Service Extension Keys** section. -1. Click on the **Show link** to reveal one of the keys, then copy its value. - -![App service extension keys](./media/channels/direct-line-extension-extension-keys.png) - -### Enable the Direct Line App Service Extension - -1. In your browser, navigate to the [Azure portal](https://portal.azure.com/) -1. In the Azure portal, locate the **Azure App Service** resource page for the Web App where your bot is or will be hosted -1. Click on **Configuration**. Under the *Application settings* section, add the following new settings: - - |Name|Value| - |---|---| - |DirectLineExtensionKey|| - |DIRECTLINE_EXTENSION_VERSION|latest| - -1. Within the *Configuration* section, click on the **General** settings section and turn on **Web sockets** -1. Click on **Save** to save the settings. This restarts the Azure App Service. - -### Confirm Direct Line App Extension and the Bot are Initialized - -1. In your browser, navigate to https://.azurewebsites.net/.bot. -If everything is correct, the page will return this JSON content: `{"k":true,"ib":true,"ob":true,"initialized":true}`. This is the information you obtain when **everything works correctly**, where - - - **k** determines whether Direct Line App Service Extension (ASE) can read an App Service Extension Key from its configuration. - - **initialized** determines whether Direct Line ASE can use the App Service Extension Key to download the bot metadata from Azure Bot Service - - **ib** determines whether Direct Line ASE can establish an inbound connection with the bot. - - **ob** determines whether Direct Line ASE can establish an outbound connection with the bot. +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A Node.js bot deployed in Azure. +- Bot Framework SDK for Node.js, 4.7 or later. + +## Enable Direct Line App Service extension + +This section describes how to enable the Direct Line App Service extension using the App Service extension key from your bot's Direct Line channel configuration. + +## Update bot code + +To allow your app to use the **Direct Line App Service Extension Named Pipe**: + +1. Edit your bot's `index.js` file. + 1. Locate the line where you create the bot's adapter. + 1. After the adapter is created, add the following statement, which will pull the App Service name from the environment and instruct the adapter to connect to the appropriate named pipe. + + - If your bot uses the `CloudAdapter` (recommended): + + ```javascript + adapter.connectNamedPipe( + process.env.APPSETTING_WEBSITE_SITE_NAME + '.directline', + async (context) => { + await myBot.run(context); + }, + process.env.MicrosoftAppId, + AuthenticationConstants.ToChannelFromBotOAuthScope); + ``` + + - If your bot uses the deprecated `BotFrameworkAdapter`: + + ```javascript + adapter.useNamedPipe(async (context) => { + await myBot.run(context); + }, + process.env.APPSETTING_WEBSITE_SITE_NAME + '.directline' + ); + ``` + + 1. Save your changes. + +1. Edit your bot's `web.config` file to add the `AspNetCore` handler and rule needed by Direct Line App Service extension to service requests. + + 1. Edit your bot's `web.config` file. + 1. Change the `webSocket` tag's `enabled` attribute to `true`. + + ```xml + + ``` + + 1. In the `` section, add a registration for the following handler. + + ```xml + + ``` + + 1. In the `` section, add the following rule to the rules list. + + ```xml + + + + + + + ``` + + 1. Save your changes. + +1. Redeploy your updated bot to Azure. + +### Enable bot Direct Line App Service extension + +[!INCLUDE [enable Bot Direct Line App Service extensions](includes/directline-enable-dl-asp.md)] + +## Confirm the Direct Line extension and the bot are configured + +[!INCLUDE [Confirm the extension and the bot are configured](includes/directline-confirm-extension-bot-config.md)] + +## Troubleshooting + +[!INCLUDE [Troubleshoot Direct Line extension](includes/directline-troubleshoot.md)] + +## Next steps + +> [!div class="nextstepaction"] +> [Use Web Chat with the Direct Line App Service extension](./bot-service-channel-directline-extension-webchat-client.md) diff --git a/articles/bot-service-channel-directline-extension-vnet.md b/articles/bot-service-channel-directline-extension-vnet.md index b92002120..ec07e878b 100644 --- a/articles/bot-service-channel-directline-extension-vnet.md +++ b/articles/bot-service-channel-directline-extension-vnet.md @@ -1,55 +1,58 @@ --- -title: Use direct line app service extension within a VNET -titleSuffix: Bot Service -description: Use direct line app service extension within a VNET -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: kamrani -ms.date: 07/25/2019 +title: Use Direct Line App Service extension within a VNET +description: Use a Direct Line App Service extension with an Azure Virtual Network. Create an environment and configure an outbound connection. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms-custom: abs-meta-21q1 +ms.custom: + - evergreen --- -# Use direct line app service extension within a VNET +# Use Direct Line App Service extension within a virtual network -This article describes how to use the Direct Line App Service Extension with an Azure Virtual Network (VNET). +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article describes how to use the Direct Line App Service extension with an Azure Virtual Network (VNET). ## Create an App Service Environment and other Azure resources -1. Direct line app service extension is available on all **Azure App Services**, including those hosted within an **Azure App Service Environment**. An Azure App Service Environment provides isolation and is ideal for working within a VNET. - - Instructions for creating an external App Service Environment can be found in [Create an External App Service environment](https://docs.microsoft.com/azure/app-service/environment/create-external-ase) article. - - Instructions for creating an internal App Service Environment can be found in [Create and use an Internal Load Balancer App Service Environment](https://docs.microsoft.com/azure/app-service/environment/create-ilb-ase) article. -1. Once you have created your App Service Environment, you need to add an App Service Plan inside of it where you can deploy your bots (and thus run Direct Line App Service Extension). To do this: - - Go to https://portal.azure.com/ - - Create a new “App Service Plan” resource. +1. The Direct Line App Service extension is available on all **Azure App Services**, including those hosted within an **Azure App Service Environment**. An Azure App Service Environment provides isolation and is ideal for working within a VNET. + - Instructions for creating an external App Service Environment can be found in [Create an External App Service environment](/azure/app-service/environment/create-external-ase) article. + - Instructions for creating an internal App Service Environment can be found in [Create and use an Internal Load Balancer App Service Environment](/azure/app-service/environment/create-ilb-ase) article. +1. After you create your App Service Environment, add an App Service Plan inside of it where you can deploy your bots (and thus run Direct Line App Service extension). To do this: + - Go to the [Azure portal](https://portal.azure.com/). + - Create a new "App Service Plan" resource. - Under Region, select your App Service Environment - Finish creating your App Service Plan ## Configure the VNET Network Security Groups (NSG) -1. Direct Line App Service Extension requires an outbound connection so that it can issue HTTP requests. This can be configured as an outbound rule in your VNET NSG that is associated with the App Service Environment’s subnet. The rule that required is as follows: - -|Source|Any| -|---|---| -|Source Port|*| -|Destination|IP Addresses| -|Destination IP addresses|20.38.80.64, 40.82.248.64| -|Destination port ranges|443| -|Protocol|Any| -|Action|Allow| - - -![Direct line extension architecture](./media/channels/direct-line-extension-vnet.png) - ->[!NOTE] -> The IP addresses provided are explicitly for the preview. We will be moving to a Service Tag for Azure Bot Service later in the year which will change this configuration. - -### Configure your bot’s App Service - -For the preview, you will need to change how your Direct line app service extension communicates with Azure. This can be done by adding a new **App Service Application Setting** to your application using the portal, or the `applicationsettings.json` file: - -- Property: DirectLineExtensionABSEndpoint -- Value: https://st-directline.botframework.com/v3/extension - ->[!NOTE] -> This will only be required for the preview of Direct line app service extension. +1. Direct Line App Service extension requires an outbound connection so that it can issue HTTP requests. This can be configured as an outbound rule in your VNET NSG that is associated with the App Service Environment's subnet. The rule that required is as follows: + + | Field | Value | + |:------------------------|:----------------| + | Source | Any | + | Source Port | * | + | Destination | Service Tag | + | Destination Service Tag | AzureBotService | + | Destination port ranges | 443 | + | Protocol | Any | + | Action | Allow | + +1. If your bot uses OAuth for user sign in to Microsoft Entra ID, add a second outbound rule that's associated with `https://login.microsoftonline.com/botframework.com`: + + | Field | Value | + |:------------------------|:---------------------| + | Source | Any | + | Source Port | * | + | Destination | Service Tag | + | Destination Service Tag | AzureActiveDirectory | + | Destination port ranges | 443 | + | Protocol | Any | + | Action | Allow | diff --git a/articles/bot-service-channel-directline-extension-webchat-client.md b/articles/bot-service-channel-directline-extension-webchat-client.md index e423d89e8..b36c31af6 100644 --- a/articles/bot-service-channel-directline-extension-webchat-client.md +++ b/articles/bot-service-channel-directline-extension-webchat-client.md @@ -1,88 +1,108 @@ --- -title: Use WebChat with the direct line app service extension -titleSuffix: Bot Service -description: Use WebChat with the direct line app service extension -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: kamrani -ms.date: 07/25/2019 +title: Use Web Chat with the Direct Line App Service extension in Bot Framework SDK +description: Learn how to use Web Chat with a Direct Line App Service extension. View code that shows how to set up a direct line URL for a bot and obtain a token. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Use WebChat with the direct line app service extension +# Use Web Chat with the Direct Line App Service extension -This article describes how to use WebChat with the direct line app service extension. +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** -## Get Your Direct Line Secret +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -The first step is to find your direct line secret. You can do this by following the instructions described in [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) article. +This article describes how to use Web Chat with the Direct Line App Service extension. Web Chat version 4.9.1 or later is required for native Direct Line App Service extension support. -## Get the preview version of DirectLineJS -The preview version of DirectLineJS can be found here: -https://github.com/Jeffders/DirectLineAppServiceExtensionPreview/tree/master/libraries +## Integrate Web Chat client -## Integrate WebChat client +> [!NOTE] +> Adaptive Cards sent through the Direct Line App Service extension don't undergo the same processing as those sent through other versions of the Direct Line channel. Due to this the JSON representation of the Adaptive Card sent to Web Chat from the Direct Line App Service extension won't have default values added by the channel if the fields are omitted by the bot when the card is created. -Generally speaking, the approach is the same as before. With the exception that a new version of **WebChat** has been created that supports two-way **WebSocket** traffic, which instead of connecting to https://directline.botframework.com/ connects directly to your hosted bot. -The direct line URL for your bot will be `https://.azurewebsites.net/.bot/`, where the `/.bot/` extension is the Direct Line **endpoint** on your App Service. -If you can configure your own domain name you still must append the `/.bot/` path to access the direct line REST APIs. +Generally speaking, the approach is the same as before. With the exception that in version 4.9.1 or later of Web Chat there is built-in support for establishing a two-way _WebSocket_. This allows Web Chat to directly connect to the Direct Line App Service extension hosted with your bot instead of connecting to [https://directline.botframework.com/](bot-service-channel-connect-directline.md). +The Direct Line URL for your bot will be `https://.azurewebsites.net/.bot/`, the Direct Line _endpoint_ on your app service extension. +If you configure your own domain name, or your bot is hosted in a sovereign Azure cloud, substitute in the appropriate URL and append the `/.bot/` path to access the Direct Line App Service extension's REST APIs. -1. Exchange the secret for a token by following the instructions in the [Authentication](https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0) article. But, instead of obtaining a token at this location: `https://directline.botframework.com/v3/directline/tokens/generate`, you generate the token directly from your Direct Line App Service Extension at this location: `https://.azurewebsites.net/.bot/v3/directline/tokens/generate`. +1. Exchange the secret for a token by following the instructions in the [Authentication](/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0&preserve-view=true) article. Instead of obtaining a token at `https://directline.botframework.com/v3/directline/tokens/generate`, you'll generate the token directly from your Direct Line App Service extension at `https://.azurewebsites.net/.bot/v3/directline/tokens/generate`. -1. Once you have a token, you can update the webpage that uses WebChat with these changes: +1. For an example that shows how to fetch a token see [Web Chat Samples](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/01.getting-started/i.protocol-direct-line-app-service-extension). -```html - - - - Direct Line Streaming Sample - - - - - - -
- - - - -``` + } + + body { + margin: 0; + } + + #webchat { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.05); + height: 100%; + margin: auto; + max-width: 480px; + min-width: 360px; + } + + + +
+ + + + ``` + + > [!TIP] + > In the JavaScript bot implementation, make sure that WebSockets are enabled in the **web.config** file, as shown below. + + ```xml + + + + ... + + + ``` diff --git a/articles/bot-service-channel-directline-extension.md b/articles/bot-service-channel-directline-extension.md index fa90e350b..b14d786ac 100644 --- a/articles/bot-service-channel-directline-extension.md +++ b/articles/bot-service-channel-directline-extension.md @@ -1,45 +1,51 @@ --- -title: Direct line app service extension +title: Direct Line App Service extension titleSuffix: Bot Service -description: Direct line app service extension features -services: bot-service -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.author: kamrani -ms.date: 07/09/2019 +description: Become familiar with the Direct Line App Service extension. See how to use streaming extensions to connect directly to hosted bots. View additional resources. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: article +ms.custom: + - evergreen --- -# Direct Line App Service Extension +# Direct Line App Service extension -[!INCLUDE[applies-to-v4](includes/applies-to.md)] +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** -> [!WARNING] -> The **Direct Line App Service Extension** is in public **preview**. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -The direct line app service extension allows clients to connect directly with the host, where the bot is located. This provides workload isolation and, in some cases, improved performance. The following picture shows the overall architecture: +The Direct Line App Service extension allows clients to connect directly with the host, where the bot is located. It runs inside the same subscription, app service, and Azure network as your bot and provides network isolation and, in some cases, improved performance. The client application uses [WebSocket protocol](https://tools.ietf.org/html/rfc6455) to communicate with the bot. -![Direct line extension architecture](./media/channels/direct-line-extension-architecture.png) +The Direct Line App Service extension is only supported in a Windows App Service, and not currently supported in a Linux App Service. -The direct line app service extension adds a new set of streaming extensions to the Bot Framework protocol, which replace HTTP for exchanging messages with a transport that allows bidirectional requests to be sent over a **persistent WebSocket**. +The following picture shows the overall architecture: -Before streaming extensions, the Direct Line API offered one way for a client to send Activities to Direct Line and two ways for a client to retrieve Activities from Direct Line. The messages were sent via an HTTP POST, and received by either an HTTP GET (polling) or by opening a WebSocket to receive ActivitySets. -Streaming extensions expand on the use of the WebSocket an allows **all messaging communication** to be sent on that WebSocket. Streaming extensions can also be used between channel services and the bot. +:::image type="content" source="media/channels/direct-line-extension-architecture.png" alt-text="Diagram illustrating the Direct Line App Service extension architecture."::: + +> [!NOTE] +> If you don't require network isolation and want to use the standard channel over the HTTPS protocol, refer to [Connect a bot to Direct Line](bot-service-channel-connect-directline.md). -The direct line app service extension is pre-installed on all instances of Azure App Services in every data center around the world. It is maintained and managed by Microsoft without additional deployment work for the customer. It is disabled on Azure App Services by default, but it can be easily turned on so that it can connect to your hosted bot. +The Direct Line App Service extension adds a new set of streaming extensions to the Bot Framework protocol, replacing exchanging messages via HTTP with a transport that allows bidirectional requests to be sent over a _persistent WebSocket_. + +Before streaming extensions, the Direct Line API offered one way for a client to send Activities to Direct Line and two ways for a client to retrieve Activities from Direct Line. The messages were sent via an HTTP POST, and received by either an HTTP GET (polling) or by opening a WebSocket to receive ActivitySets. +Streaming extensions expand on the use of the WebSocket and allow all messaging communication to be sent on that WebSocket. Streaming extensions can also be used between channel services and the bot. +The Direct Line App Service extension is pre-installed on all instances of Azure App Services in every data center around the world. It's maintained and managed by Microsoft without additional deployment work for the customer. It's disabled on Azure App Services by default, but it can be easily turned on to connect to your hosted bot. ## See Also |Name|Description| |---|---| -|[Configure .NET bot for extension](bot-service-channel-directline-extension-net-bot.md)|Update a .NET bot to work with **named pipes**, and enable the Direct Line App Service Extension in the **Azure App Service** resource where the bot is hosted. | -|[Configure Node.js bot for extension](bot-service-channel-directline-extension-node-bot.md)|Update a Node.js bot to work with **named pipes** and enable the Direct Line App Service Extension in the **Azure App Service** resource where the bot is hosted. | -|[Create .NET client with Extension](bot-service-channel-directline-extension-net-client.md)|Create a .NET client in C# which connects to the direct line app service extension| -|[Use extension with WebChat](bot-service-channel-directline-extension-webchat-client.md)|Use WebChat with the direct line app service extension| -|[Use extension within VNET](bot-service-channel-directline-extension-vnet.md)|Use the direct line app service extension with an Azure Virtual Network (VNET)| +|[Configure .NET bot for extension](bot-service-channel-directline-extension-net-bot.md)|Update a .NET bot to work with named pipes, and enable the Direct Line App Service extension in the Azure App Service resource where the bot is hosted. | +|[Configure Node.js bot for extension](bot-service-channel-directline-extension-node-bot.md)|Update a Node.js bot to work with named pipes and enable the Direct Line App Service extension in the Azure App Service resource where the bot is hosted. | +|[Create .NET client with extension](bot-service-channel-directline-extension-net-client.md)|Create a .NET client in C# which connects to the Direct Line App Service extension.| +|[Use extension with Web Chat](bot-service-channel-directline-extension-webchat-client.md)|Use Web Chat with the Direct Line App Service extension.| +|[Use extension within VNET](bot-service-channel-directline-extension-vnet.md)|Use the Direct Line App Service extension with an Azure Virtual Network (VNET).| ## Additional resources - [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) -- [Connect a bot to Direct Line Speech](bot-service-channel-connect-directlinespeech.md) diff --git a/articles/bot-service-channel-directline.md b/articles/bot-service-channel-directline.md index 0b891a443..ad1520653 100644 --- a/articles/bot-service-channel-directline.md +++ b/articles/bot-service-channel-directline.md @@ -1,38 +1,39 @@ --- title: About Direct Line channel titleSuffix: Bot Service -description: Direct Line channel features -services: bot-service -author: ivorb -manager: kamrani -ms.service: bot-service -ms.topic: conceptual -ms.date: 11/01/2019 -ms.author: kamrani +description: Learn about the Bot Framework Direct Line three channels. Select the channel to use to integrate bots into mobile apps, webpages, and other applications. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: Daniel.Evans +ms.service: azure-ai-bot-service +ms.topic: overview +ms.custom: + - evergreen --- # About Direct Line -The Bot Framework Direct Line channel is an easy way to integrate your bot into your mobile app, webpage, or other application. -Direct Line is available in three forms: -- Direct Line service – a global, robust service for most developers -- Direct Line App Service Extensions – dedicated Direct Line functionality for security and performance (available in public preview) -- Direct Line Speech – optimized for high-performance speech (GA) +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] +The Bot Framework offers multiple channels with the Direct Line branding. It's important that you select the version that best fits the conversational AI experience you're designing. -You can choose which offering of Direct Line is best for you by evaluating the features each offers and the needs of your solution. -Over time these offerings will be simplified. +- **Direct Line**. This is the standard channel offering of Direct Line. It works by default with bot templates via the [Azure portal](https://ms.portal.azure.com/), bots from the [Bot Builder Samples](https://github.com/Microsoft/BotBuilder-Samples/blob/main/README.md), and bots created with the [Azure CLI](/cli/azure/what-is-azure-cli). This is the Direct Line best suited in the majority of the cases. See [Connect a bot to Direct Line](bot-service-channel-connect-directline.md). +- **Direct Line App Service Extension**. It runs inside the same subscription, App Service, and Azure network as your bot. If you have network isolation requirements, this version of Direct Line may be ideal. Bots and clients require special modifications to work with the Direct Line App Service Extension to ensure traffic never leaves the isolated network. See [Direct Line App Service Extension](bot-service-channel-directline-extension.md). -| | Direct Line | Direct Line App Service Extension | Direct Line Speech | -|----------------------------|-------------|-----------------------------------|--------------------| -| Availability and Licensing | General availability | Public preview, no SLA | GA | -| Speech recognition and text-to-speech performance | Standard | Standard | High performance | -| Supports legacy web browsers | Yes | At GA | Yes | -| Bot Framework SDK support | All v3, v4 | v4.63+ required | v4.63+ required | -| Client SDK support | JS, C# | JS, C# | C++, C#, Unity, JS| -| Works with Web Chat | Yes | Yes | No| -| VNET | No | Preview | No | +You can choose which offering of Direct Line is best for you by evaluating the features each offers and the needs of your solution. +Over time these offerings will be simplified. +| Feature | Direct Line | Direct Line App Service Extension | +|--------------|-------------|-----------------------------------| +| Availability and Licensing | GA | GA | +| Speech recognition and text-to-speech performance | Standard | Standard | +| Supports legacy web browsers | Yes | Yes | +| Bot Framework SDK support | All v3, v4 | v4.6.3+ required | +| Client SDK support | JS, C# | JS, C# | +| Works with Web Chat | Yes | Yes | +| VNET | No | Yes | ## Additional resources + - [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) -- [Connect a bot to Direct Line Speech](bot-service-channel-connect-directlinespeech.md) +- [Direct Line App Service Extension](bot-service-channel-directline-extension.md) diff --git a/articles/bot-service-channel-omnichannel.md b/articles/bot-service-channel-omnichannel.md new file mode 100644 index 000000000..ab96924bf --- /dev/null +++ b/articles/bot-service-channel-omnichannel.md @@ -0,0 +1,48 @@ +--- +title: Connect a Bot Framework bot to Omnichannel +description: Learn how to configure bots to use the omnichannel capabilities of the Chat Add-in for Dynamics 365 Customer Service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +--- + +# Connect a bot to the Omnichannel channel + +The Omnichannel channel lets you connect your bot to omnichannel capabilities of the Chat Add-in for Dynamics 365 Customer Service. + +## Prerequisites + +- Knowledge of [Basics of the Bot Framework Service](v4sdk/bot-builder-basics.md) and how to [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md). +- The bot to connect to the channel. +- If you don't have an Azure account, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. + +## Connect your bot to the Omnichannel channel + +To register your bot with your Omnichannel for Customer Service environment: + +1. In the Azure portal, go to your bot resource. +1. Open the **Channels** blade and select **Omnichannel**. +1. On the **Configure Omnichannel** blade, select **Apply**. +1. Once you see the success message, close the **Configure Omnichannel** blade. + +## Integrate your bot with the omnichannel feature + +Perform the final steps to set up your bot user and routing in your Dynamics 365 organization. + +For instructions, see [Integrate an Azure bot](/dynamics365/customer-service/configure-bot) in the Omnichannel for Customer Service docs. + +## Additional information + +You can design your bot so that it can hand off the conversation to a human agent. For more information, see [Transition conversations from bot to human](bot-service-design-pattern-handoff-human.md). + +## Next steps + +- For information about channel support in the Bot Connector Service, see [Connect a bot to channels](bot-service-manage-channels.md). +- For information about building bots, see [How bots work](v4sdk/bot-builder-basics.md) and the [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md) quickstart. +- For information about deploying bots, see [Deploy your bot](bot-builder-deploy-az-cli.md) and [Set up continuous deployment](bot-service-build-continuous-deployment.md). diff --git a/articles/bot-service-channels-reference.md b/articles/bot-service-channels-reference.md index 52a87605c..a88d4bc7d 100644 --- a/articles/bot-service-channels-reference.md +++ b/articles/bot-service-channels-reference.md @@ -1,288 +1,211 @@ --- title: Channels reference -description: Bot Framework Channels reference +description: View reference information on bot channels. See which channels generate which events and support which cards. See the number of actions that channels support. keywords: channels reference, bot builder channels, bot framework channels -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/03/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - evergreen --- -# Categorized activities by channel - -The following tables show what events (Activities on the wire) can come from which channels. - -This is the key for the tables: - -Symbol | Meaning -:------------------:|:------------------------------------------------ -:white_check_mark: |The Bot should expect to receive this Activity -:x: |The Bot should **never** expect to receive this Activity -:white_large_square:|Currently undetermined whether the Bot can receive this - -Activities can meaningfully be split into separate categories. For each category we have a table of possible Activities. - -Conversational --------------- - - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :----------------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -Message | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: -MessageReaction | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: - -- All Channels send Message Activities. -- When using a Dialog, Message Activities should generally always be passed onto the Dialog. -- This is probably not true of the MessageReaction although they are very much part of the conversation. -- There are logically two types of MessageReaction: Added and Removed - +# Channels reference + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article outlines channel support for various Bot Framework features: + +- The activity types each channel can send or receive. +- The card types each channel can display, including Adaptive Cards. +- Card action and suggested action support on each channel. +- A general classification of the different activity types. + +For detailed information about the structure of activities and cards at the protocol level, +see the Bot Framework [activity](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) +and [card](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md) schemas. + +Adaptive Cards is a separate technology. For more information, see [adaptivecards.io](https://adaptivecards.io/). + +## Activity support by channel + +The following table indicates whether a given channel can send a given activity type to your bot. +Within the table, the following terms have the following meanings. + +| Term | Meaning | +|:-------------|:------------------------------------------------------| +| Yes | The bot can receive this activity from the channel. | +| No | The bot can't receive this activity from the channel. | +| Undetermined | Currently undetermined. | + +| Channel | Contact relation update | Conversation update | End of conversation | Event | Installation update | Invoke | Message | Message reaction | Message update | Message delete | Typing | +|:--------------------------------------------|:------------------------|:--------------------|:--------------------|:-------------|:--------------------|:-------|:--------|:-----------------|:---------------|:---------------|:-------| +| Alexa | No | No | Yes | Yes | No | No | Yes | No | No | No | No | +| Azure Communication Services | No | Yes | No | Yes | No | No | Yes | No | Yes | Yes | Yes | +| Direct Line | No | Yes | Yes | Yes | Yes | No | Yes | No | No | No | Yes | +| Email | No | No | No | Undetermined | No | No | Yes | No | No | No | No | +| Facebook | No | Yes | No | Yes | No | No | Yes | Yes | No | No | No | +| GroupMe | No | Yes | No | Undetermined | No | No | Yes | No | No | No | No | +| LINE | No | Yes | No | Yes | No | No | Yes | No | No | No | No | +| Microsoft Teams | No | Yes | No | Undetermined | No | Yes | Yes | Yes | Yes | Yes | No | +| Omnichannel | | | | | | | Yes | | | | | +| Outlook (preview) | | | | | | | Yes | | | | | +| Search (preview) | | | | | | | Yes | | | | | +| Slack | No | Yes | No | Undetermined | No | No | Yes | No | Yes | Yes | No | +| Telegram | No | Yes | No | Undetermined | No | No | Yes | No | Yes | Undetermined | No | +| Twilio (SMS) | No | No | No | Undetermined | No | No | Yes | No | No | No | No | +| Web Chat | No | Yes | Yes | Yes | Yes | No | Yes | No | No | No | Yes | + +Support for `event` and `invoke` activities varies by the activity's name and varies by channel. + +## Card support by channel + +The following table indicates whether a given channel can render a given card type. +Even if a channel can render a card type, the channel may not support all features on the card. +Before releasing your bot, test the behavior of each card your bot can send. + +Within the table, the following terms have the following meanings. + +| Term | Meaning | +|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Yes | The card is supported on this channel; however, any given channel may only support a subset of card actions or may limit the number of actions allowed on each card. | +| No | The card isn't supported on this channel. | +| Partial | Partial support. This channel might not display the card if the card contains inputs or buttons. Level of support varies by channel. | +| Image | Card is converted to image. | +| Text | Card is converted to unformatted text. Links may not be clickable, images may not display, and media may not be playable. Level of support varies by channel. | + +| Channel | Adaptive Card | Animation card | Audio card | Hero card | Receipt card | Sign-in card | Thumbnail card | Video card | +|:--------------------------------------------|:---------------|:---------------|:-----------|:----------|:-------------|:-------------|:---------------|:-----------| +| Alexa | No | No | No | Yes | No | Yes | No | No | +| Azure Communication Services | Yes* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Email | Image | Text | Text | Yes | Yes | Yes | Yes | Text | +| Facebook | Image, partial | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| GroupMe | Image | Text | Text | Text | Text | Text | Text | Text | +| LINE | Image, partial | Yes | Text | Yes | Yes | Yes | Yes | Text | +| Microsoft Teams | Yes | No | No | Yes | Yes | Yes | Yes | No | +| Omnichannel | | | | | | | | | +| Outlook (preview) | | | | | | | | | +| Search (preview) | | | | | | | | | +| Slack | Image | Yes | Text | Text | Yes | Yes | Text | Text | +| Telegram | Image, partial | Yes | Text | Yes | Yes | Yes | Yes | Yes | +| Twilio (SMS) | Image | Text | No | Text | Text | Text | Text | No | +| Web Chat | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | + +> [!NOTE] +> +> - The Direct Line channel technically supports all cards, but it's up to the client to implement them. +> - *For Azure Communication Services Chat, Adaptive cards are only supported within Azure Communication Services use cases, and not for Azure Communication Services to Teams use cases. + +## Card action support by channel + +The following table shows the maximum number of suggested actions and card actions that a given channel supports. +A value of "None" indicates that the action type isn't supported in the channel. + +| Channel | Suggested actions | Card actions | +|:--------------------------------------------|:-----------------:|:------------:| +| Alexa | None | None | +| Azure Communication Services | | | +| Direct Line | 100 | 100 | +| Email | None | None | +| Facebook | 11 | 3 | +| GroupMe | None | None | +| LINE | 13 | 99 | +| Microsoft Teams | None | 3 | +| Omnichannel | | | +| Outlook (preview) | | | +| Search (preview) | | | +| Slack | None | 100 | +| Telegram | 100 | 100 | +| Twilio (SMS) | None | None | +| Web Chat | 100 | 100 | + +- For more information about card actions, see [Process events within rich cards](v4sdk/bot-builder-howto-add-media-attachments.md#process-events-within-rich-cards) in the _Add media to messages_ article. +- For more information about suggested actions, see how to [Use buttons for input](v4sdk/bot-builder-howto-add-suggested-actions.md). + +## Activity categories + +Activities can be split into separate categories. +For a detailed description of each type of activity, and the information that each type of activity contains, see the [Bot Framework activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md). + +### Welcome + +This category includes the `conversationUpdate` and `contactRelationUpdate` activities. + +- Many channels send conversation update activities. + - Often, bot _welcome_ behavior is triggered by the conversation update activity. + However, producing reliable welcome behavior might require the use of conversation or user state. +- Some channels send contact relation update activities. + - If your bot uses these channels, you may need to include logic for this activity in your bot's welcome behavior. + +### Conversational + +This category includes the `message`, `messageReaction`, and `endOfConversation` activities. + +- All channels can send and receive message activities. + - For bots that use dialogs, message activities should generally be passed into the dialog. +- Some channels can send and receive message reaction activities. + - Depending on the design of your bot, you might pass message reaction activities into a dialog. + - Message reaction activities reference previous messages by ID. +- End of conversation activities signal the end of a conversation from the sender's perspective. + - End of conversation activities are used in bot-to-bot communication for skills. > [!TIP] -> "Message Reactions" are things like a "thumbs up" on a previous comment. They can happen out of order, so they can be thought of as similar to buttons. This Activity is currently sent by the Teams Channel. - - -Welcome -------- - - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -ConversationUpdate | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_large_square: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: -ContactRelationUpdate | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: +> A _message reaction_ includes things like a _thumbs up_ on a previous comment. +> They can happen out of order, so they can be thought of as similar to buttons. +> This activity type can be sent by the Teams channel. -- It is common for Channels to send ConversationUpdate Activities. -- There are logically two types of MessageReaction: Added and Removed -- It is very tempting to assume bot "Welcome" behavior can be simply implemented by wiring up ConversationUpdate.Added and this sometimes works. -- However, this is a simplification, in order to produce a reliable "Welcome" behavior the bot implementation may also need to use state. +### Message update and delete +This category includes the `messageUpdate` and `messageDelete` activities. -Application Extensibility -------------------------- +- Teams supports the message update and delete activities. - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -Event.* | :white_large_square: | :white_check_mark: | :white_check_mark: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -Event.CreateConversation | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -Event.ContinueConversation | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: +### Application extensibility -- Event Activities are an extensibility mechanism in Direct Line (_aka Web Chat_). -- An application that owns both the client and server may chose to tunnel their own events through the service using this Event Activity. +This category includes the `event` and `invoke` activities. +The meaning of the activity is defined by its `name` field, which is meaningful within the scope of a channel. +- An application that owns both the client and server can use event activities to communicate programmatic information between the client and server. + - Event activities, like most activity types, are asynchronous. + - Direct Line and Web Chat use event activities as an extensibility mechanism. +- Invoke activities are specific to an application and not something a client would define. + - Invoke activities, unlike other activity types, are synchronous. + (Invoke is currently the only activity type that triggers a request-reply behavior on the bot.) + - Microsoft Teams uses invoke activities and defines a few Teams-specific invoke activities. -Microsoft Teams ------------------- +#### Authentication - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -Invoke.TeamsVerification | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: -Invoke.ComposeResponse | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: +For the OAuth prompt to work with dialogs, the `TeamsVerification` invoke activity must be forwarded to the dialog. -- Along with a number of the other typed Activities, Microsoft Teams defines a few Teams specific Invoke Activities. -- Invoke Activities are specific to an application and not something a client would define. -- There is no general notion of Invoke specific subtypes of the activity. -- Invoke is currently the only Activity that triggers a request-reply behavior on the bot. +### Uncategorized -This is very important: if using Dialogs for the OAuth Prompt to work the Invoke.TeamsVerification Activity must be forwarded to the Dialog. +The `installationUpdate`, `typing`, and `handoff` activities don't meaningfully fit into the other categories. +- Installation update activities represent an installation or uninstallation of a bot within an organizational unit of a channel. +- Typing activities represent ongoing input from a user or a bot. +- Handoff activities request or signal a change in focus between elements inside a bot. The _handoff_ activity is different from an _event_ activity that has the name "handoff". -Message Update --------------- +### Out of use (includes payment specific invoke) - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -MessageUpdate | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_large_square: | :x: | :x: | :x: | :x: -MessageDelete | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_large_square: | :x: | :x: | :x: | :x: +These activity types are no longer in use: -- Message Update is currently supported by Teams. +- `deleteUserData` +- `handoff` +- `ping` +- `Address` invoke +- `PaymentRequest` invoke +## Additional information -OAuth -------- +All channels can send and receive `message` activities. - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -Event.TokenResponse| :white_large_square: | :white_check_mark: | :white_check_mark: | :x: | :white_large_square: | :white_large_square: | :white_large_square: | :x: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: - -This is very important: if using Dialogs for the OAuth Prompt to work the Event.TokenResponse Activity must be forwarded to the Dialog. - - -Uncategorized -------------- - - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -EndOfConversation | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -InstallationUpdate | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -Typing | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -Handoff | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: - - -Out of Use (includes Payment specific Invoke) ---------------------------------------------- -- DeleteUserData -- Invoke.PaymentRequest -- Invoke.Address -- Ping - ---- - -## Summary of Activities supported per Channel - -Cortana -------- -- Message -- ConversationUpdate -- _Event.TokenResponse_ -- _EndOfConversation (when the window closes?)_ - -Direct Line --------- -- Message -- ConversationUpdate -- Event.TokenResponse -- Event.* -- _Event.CreateConversation_ -- _Event.ContinueConversation_ - -Email ------ -- Message - -Facebook --------- -- Message -- _Event.TokenResponse_ - -GroupMe -------- -- Message -- ConversationUpdate -- _Event.TokenResponse_ - -Kik ---- -- Message -- ConversationUpdate -- _Event.TokenResponse_ - -Teams ------ -- Message -- ConversationUpdate -- MessageReaction -- MessageUpdate -- MessageDelete -- Invoke.TeamsVerification -- Invoke.ComposeResponse - - -Slack ------ -- Message -- ConversationUpdate -- _Event.TokenResponse_ - - -Skype ------ -- Message -- ContactRelationUpdate -- _Event.TokenResponse_ - - -Skype Business --------------- -- Message -- ContactRelationUpdate -- _Event.TokenResponse_ - - -Telegram --------- -- Message -- ConversationUpdate -- _Event.TokenResponse_ - - -Twilio ------- -- Message - -## Summary Table All Activities to All Channels - - \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Teams | Slack | Skype | Skype Business | Telegram | Twilio -:---------------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :------------: | :------: | :----: -Message | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: -MessageReaction | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: -ConversationUpdate | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_large_square: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: -ContactRelationUpdate | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: -Event.* | :white_large_square: | :white_check_mark: | :white_check_mark: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -Event.CreateConversation | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -Event.ContinueConversation | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -Invoke.TeamsVerification | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: -Invoke.ComposeResponse | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: -MessageUpdate | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_large_square: | :x: | :x: | :x: | :x: -MessageDelete | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :white_check_mark: | :white_large_square: | :x: | :x: | :x: | :x: -Event.TokenResponse | :white_large_square: | :white_check_mark: | :white_check_mark: | :x: | :white_large_square: | :white_large_square: | :white_large_square: | :x: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: | :white_large_square: -EndOfConversation | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -InstallationUpdate | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -Typing | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: -Handoff | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: | :x: - -## Web Chat -Web Chat will send: -- "message": with "text" and/or "attachments" -- "event": with "name" and "value" (as JSON/string) -- "typing": if the user set an option, namely "sendTypingIndicator" -Web Chat will not send "contactRelationUpdate". And Web Chat do not support "messageReaction", no one explicitly ask us to support this feature. - -By default, Web Chat will render: -- "message": will render as either carousel or stacked, depends on the option in the activity -- "typing": will render for 5s and hide it, or until next activity come in -- "conversationUpdate": will hide -- "event": will hide -- Others: will show a warning box (we never see it in production) -You can modify this render pipeline to add, remove, or replace any custom render. - -You can use Web Chat to send any activity type and payload, we neither document nor recommend this feature. -You should use "event" activity instead. - -## Action support by channel - -The following table shows the maximum number of Suggested Actions and Card Actions that are supported in each channel. The :x: indicates that the action is not supported at all in the specified channel. - -| \ | Cortana | Direct Line | Direct Line (Web Chat) | Email | Facebook | GroupMe | Kik | Line | Teams | Slack | Skype | Skype Business | Telegram | Twilio | -| :---------------- | :-----: | :---------: | :--------------------: |:----: | :------: | :-----: | :-----: | :---: | :---: | :---: | :---: | :------------: | :------: | :----: | -| Suggested Actions | :x: | 100 | 100 | :x: | 10 | :x: | 20 | 13 | :x: | 100 | 10 | :x: | 100 | :x: | -| Card Actions | 100 | 100 | 100 | :x: | 3 | :x: | 20 | 99 | 3 | 100 | 3 | :x: | :x: | :x: | - -For more information about the numbers shown in the above table, refer to channel support code listed [here](https://aka.ms/channelactions). - -For more information on _Suggested Actions_, refer to the [Use button for input](https://aka.ms/howto-add-buttons) article. - -For more information on _Card Actions_, refer to the [Send a hero card](https://aka.ms/howto-add-media#send-a-hero-card) section of the _Add media to messages_ article. - -## Card Support by Channel - -| Channel | Adaptive Card | Animation Card | Audio Card | Hero Card | Receipt Card | Signin Card | Thumbnail Card | Video Card | -|:-------:|:-------------:|:--------------:|:----------:|:---------:|:------------:|:-----------:|:--------------:|:----------:| -|Cortana|✔|❌|❌|❌|✔|✔|✔|❌| -|Email|🔶|🌐|🌐|✔|✔|✔|✔|🌐| -|Facebook|⚠🔶|✔|❌|✔|✔|✔|✔|❌| -|GroupMe|🔶|🌐|🌐|🌐|🌐|🌐|🌐|🌐| -|Kik|🔶|✔|✔|❌|🌐|❌|✔|🌐| -|Line|⚠🔶|✔|🌐|✔|✔|✔|✔|🌐| -|Microsoft Teams|✔|❌|❌|✔|✔|✔|✔|❌| -|Skype|❌|✔|✔|✔|✔|✔|✔|✔| -|Slack|🔶|✔|🌐|🌐|✔|✔|🌐|🌐| -|Telegram|⚠🔶|✔|🌐|✔|✔|✔|✔|✔| -|Twilio|🔶|🌐|❌|🌐|🌐|🌐|🌐|❌| -|Web Chat|✔|✔|✔|✔|✔|✔|✔|✔| - -*Note: The Direct Line channel technically supports all cards, but it's up to the client to implement them* - -* ✔: Full Support -* ⚠: Partial Support - Card May Not Send if it Contains Inputs/Buttons. Varies by Channel. -* ❌: No Support -* 🔶: Card is Converted to Image -* 🌐: Card is Converted to Unformatted Text With Links and/or Images and/or Media Does Not Play in Client +> [!TIP] +> When adding support for a channel to your bot, familiarize yourself with the channel's developer docs. +> Each channel has different limitations on various aspects of a conversation. Some of the differences include: +> +> - How much time the bot has to handle each HTTP request. +> - Whether a bot can send an activity that's not in response to a specific user activity. +> - How many messages the bot can send within a given time frame. +> - How a card renders and which cards are supported. diff --git a/articles/bot-service-compliance.md b/articles/bot-service-compliance.md new file mode 100644 index 000000000..18fa6e1f2 --- /dev/null +++ b/articles/bot-service-compliance.md @@ -0,0 +1,61 @@ +--- +title: Compliance in Azure AI Bot Service - Bot Service +description: Learn how the Azure AI Bot Service helps users meet compliance obligations. See which certificates the service holds in various industries and geographic areas. +keywords: bot service, compliance +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +# Compliance in Azure AI Bot Service + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Azure AI Bot Service is a global Azure service and therefore is available to customers in all regions in the clouds where it's deployed, including: + +- Azure public cloud, which is available globally. +- Azure Government is available in four regions in the United States to US government agencies and their partners. + +To help customers meet their own compliance obligations across regulated industries and markets worldwide, Azure maintains the largest compliance portfolio in the industry in terms of both breadth (total number of offerings) and depth (number of customer-facing services in assessment scope). Azure compliance offerings are grouped into four segments - globally applicable, US Government, industry specific, and region or country/region specific. Compliance offerings are based on various types of assurances, including formal certifications, attestations, validations, authorizations, and assessments produced by independent third-party auditing firms, as well as contractual amendments, self-assessments, and customer guidance documents produced by Microsoft. + +## Azure AI Bot Service certifications + +Azure AI Bot Service is continually expanding its certification coverage. Currently, Azure AI Bot Service is certified with the following certificates: + +| Globally applicable | US Government | Industry specific | Region or country specific | +| :--- | :--- | :---| :---| +| CSA STAR Certification | [Chapter 508](/compliance/regulatory/offering-Section-508-VPATS) | HIPAA BAA | Australia IRAP | +| CSA STAR Attestation | DoD SRG Level 2 | HITRUST | [EN 301 549](/compliance/regulatory/offering-en-301-549-eu) | +| ISO 20000-1:2011 | FedRAMP Moderate | PCI DSS Level 1 |Germany C5 | +| ISO 22301:2012 |GxP (FDA 21 CFR Part 11) | [WCAG 2.1](https://cloudblogs.microsoft.com/industry-blog/government/2018/09/11/accessibility-conformance-reports/) |UK G-Cloud | +| ISO 27001:2013 | | | | +| ISO 27017:2015 | | | | +| ISO 27018:2014 | | | | +| ISO 9001:2015 | | | | +| SOC 1, 2, 3 | | | | + +To learn more about each of these compliance offerings and how they benefit you, see [Microsoft compliance offerings](/compliance/regulatory/offering-home) page. +In particular, see [FedRAMP](/compliance/regulatory/offering-fedramp) and [WCAG](https://cloudblogs.microsoft.com/industry-blog/government/2018/09/11/accessibility-conformance-reports/). + +The following table lists the certifications supported by Azure AI Bot Service in Azure Government: + +| Globally applicable | US Government | Industry specific | +| :--- | :--- | :--- | +| CSA STAR Certification | CJIS | HIPAA BAA | +| CSA STAR Attestation | DoD SRG Level 2 | PCI DSS | +| SOC 1, 2, 3 | DoD SRG Level 4 | | +| | FedRAMP High | | +| | IRS 1075 | | +| | NIST CSF | | +| | NIST SP 800-171 | | + +## Next steps + +To find out the latest compliance certifications for Azure AI Bot Service, see the [Overview of Azure compliance](/azure/compliance/offerings/). + +For more information about Microsoft certifications, see the [Azure Trust Center](https://azure.microsoft.com/overview/trusted-cloud/). diff --git a/articles/bot-service-concept-intelligence.md b/articles/bot-service-concept-intelligence.md deleted file mode 100644 index 21e4487bf..000000000 --- a/articles/bot-service-concept-intelligence.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -title: Cognitive Services - Bot Service -description: Learn how to add artificial intelligence to your bots with Microsoft Cognitive Services to make them more useful and engaging. -keywords: language understanding, knowledge extraction, speech recognition, web search -author: RobStand -ms.author: rstand -manager: rstand -ms.topic: article -ms.service: bot-service -ms.date: 12/17/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Cognitive Services - -Microsoft Cognitive Services let you tap into a growing collection of powerful AI algorithms developed by experts in the fields of computer vision, speech, natural language processing, knowledge extraction, and web search. The services simplify a variety of AI-based tasks, giving you a quick way to add state-of-the-art intelligence technologies to your bots with just a few lines of code. The APIs integrate into most modern languages and platforms. The APIs are also constantly improving, learning, and getting smarter, so experiences are always up to date. - -Intelligent bots respond as if they can see the world as people see it. They discover information and extract knowledge from different sources to provide useful answers, and, best of all, they learn as they acquire more experience to continuously improve their capabilities. - -## Language understanding - -The interaction between users and bots is mostly free-form, so bots need to understand language naturally and contextually. The Cognitive Service Language APIs provide powerful language models to determine what users want, to identify concepts and entities in a given sentence, and ultimately to allow your bots to respond with the appropriate action. The five APIs support several text analytics capabilities, such as spell checking, sentiment detection, language modeling, and extraction of accurate and rich insights from text. - -Cognitive Services provides these APIs for language understanding: - -- The Language Understanding Intelligent Service (LUIS) is able to process natural language using pre-built or custom-trained language models. More details can be found on [Language understanding for bots](v4sdk/bot-builder-concept-luis.md) - -- The Text Analytics API detects sentiment, key phrases, topics, and language from text. - -- The Bing Spell Check API provides powerful spell check capabilities, and is able to recognize the difference between names, brand names, and slang. - -- The Text Analytics Models in Azure Machine Learning Studio allows you to build and operationalize text analytics models such as lemmatization and text-preprocessing. These models can help you address issues such as document classification or sentiment analysis problems. - -Learn more about [language understanding][language] with Microsoft Cognitive Services. - -## Knowledge extraction - -Cognitive Services provides four knowledge APIs that enable you to identify named entities or phrases in unstructured text, add personalized recommendations, provide auto-complete suggestions based on natural interpretation of user queries, and search academic papers and other research like a personalized FAQ service. - -- The Entity Linking Intelligence Service annotates unstructured text with the relevant entities mentioned in the text. Depending on the context, the same word or phrase may refer to different things. This service understands the context of the supplied text and will identify each entity in your text. - -- The Knowledge Exploration Service provides natural language interpretation of user queries and returns annotated interpretations to enable rich search and auto-completion experiences that anticipate what the user is typing. Instant query completion suggestions and predictive query refinements are based on your own data and application-specific grammars to enable your users to perform fast queries. - -- The Academic Knowledge API returns academic research papers, authors, journals, conferences, topics, and universities from the Microsoft Academic Graph. Built as a domain-specific example of the Knowledge Exploration Service, the Academic Knowledge API provides a knowledge base using a graph-like dialog with search capabilities over hundreds of millions of research-related entities. Search for a topic, a professor, a university, or a conference, and the API will provide relevant publications and related entities. The grammar also supports natural queries like "Papers by Michael Jordan about machine learning after 2010". - -- The QnA Maker is an easy-to-use REST API and web-based service that trains AI to respond to users’ questions in a natural, conversational way. With optimized machine learning logic and the ability to integrate industry-leading language processing, QnA Maker distills semi-structured data like question and answer pairs into distinct, helpful answers. - -Learn more about [knowledge extraction][knowledge] with Microsoft Cognitive Services. - -## Speech recognition and conversion - -Use the Speech APIs to add advanced speech skills to your bot that leverage industry-leading algorithms for speech-to-text and text-to-speech conversion, as well as speaker recognition. The Speech APIs use built-in language and acoustic models that cover a wide range of scenarios with high accuracy. - -For applications that require further customization, you can use the Custom Recognition Intelligent Service (CRIS). This allows you to calibrate the language and acoustic models of the speech recognizer by tailoring it to the vocabulary of the application, or even to the speaking style of your users. - -There are three Speech APIs available in Cognitive Services to process or synthesize speech: - -- The Bing Speech API provides speech-to-text and text-to-speech conversion capabilities. -- The Custom Recognition Intelligent Service (CRIS) allows you to create custom speech recognition models to tailor the speech-to-text conversion to an application's vocabulary or user's speaking style. -- The Speaker Recognition API enables speaker identification and verification through voice. - -The following resources provide additional information about adding speech recognition to your bot. - -* [Bot Conversations for Apps video overview](https://channel9.msdn.com/events/Build/2017/P4114) -* [Microsoft.Bot.Client library for UWP or Xamarin apps](https://aka.ms/bot-client) -* [Bot Client Library Sample](https://aka.ms/trivia-bot-speech-sample) -* [Speech-enabled WebChat Client](https://aka.ms/BotFramework-WebChat) - -Learn more about [speech recognition and conversion][speech] with Microsoft Cognitive Services. - -## Web search - -The Bing Search APIs enable you to add intelligent web search capabilities to your bots. With a few lines of code, you can access billions of webpages, images, videos, news, and other result types. You can configure the APIs to return results by geographical location, market, or language for better relevance. You can further customize your search using the supported search parameters, such as Safesearch to filter out adult content, and Freshness to return results according to a specific date. - -There are five Bing Search APIs available in Cognitive Services. - -- The Web Search API provides web, image, video, news and related search results with a single API call. - -- The Image Search API returns image results with enhanced metadata (dominant color, image kind, etc.) and supports several image filters to customize the results. - -- The Video Search API retrieves video results with rich metadata (video size, quality, price, etc.), video previews, and supports several video filters to customize the results. - -- The News Search API finds news articles around the world that match your search query or are currently trending on the Internet. - -- The Autosuggest API offers instant query completion suggestions to complete your search query faster and with less typing. - -Learn more about [web search][search] with Microsoft Cognitive Services. - -## Image and video understanding - -The Vision APIs bring advanced image and video understanding skills to your bots. -State-of-the-art algorithms allow you to process images or videos and get back information you can transform into actions. For example, you can use them to recognize objects, people's faces, age, gender or even feelings. - -The Vision APIs support a variety of image understanding features. They can identify mature or explicit content, estimate and accent colors, categorize the content of images, perform optical character recognition, and describe an image with complete English sentences. The Vision APIs also support several image and video processing capabilities, such as intelligently generating image or video thumbnails, or stabilizing the output of a video. - -Cognitive Services provide four APIs you can use to process images or videos: - -- The Computer Vision API extracts rich information about images (such as objects or people), determines if the image contains mature or explicit content, and processes text (using OCR) in images. - -- The Emotion API analyzes human faces and recognizes their emotion across eight possible categories of human emotions. - -- The Face API detects human faces, compares them to similar faces, and can even organize people into groups according to visual similarity. - -- The Video API analyzes and processes video to stabilize video output, detects motion, tracks faces, and can generate a motion thumbnail summary of the video. - -Learn more about [image and video understanding][vision] with Microsoft Cognitive Services. - -## Additional resources - -You can find comprehensive documentation of each product and their corresponding API references in the Cognitive Services documentation. - -[language]: https://docs.microsoft.com/azure/cognitive-services/luis/home -[search]: https://docs.microsoft.com/azure/cognitive-services/bing-web-search/search-the-web -[vision]: https://docs.microsoft.com/azure/cognitive-services/computer-vision/home -[knowledge]: https://docs.microsoft.com/azure/cognitive-services/kes/overview -[speech]: https://docs.microsoft.com/azure/cognitive-services/speech/home -[location]: https://docs.microsoft.com/azure/cognitive-services/ diff --git a/articles/bot-service-concept-templates.md b/articles/bot-service-concept-templates.md deleted file mode 100644 index 7c3818790..000000000 --- a/articles/bot-service-concept-templates.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Bot Service templates - Bot Service -description: Learn about the different templates you can use when you create a bot with Bot Service. -author: robstand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Bot Service templates - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -Bot Service includes five templates to help you get started with building bots. These templates provide a fully functional bot out of the box to help you get started quickly. When you [create a bot](bot-service-quickstart.md), you choose a template and the SDK language for your bot. - -Each template provides a starting point that is based on a core capability for a bot. - -## Basic bot -To create a bot that uses dialogs to respond to user input, choose the Basic template. The **Basic** template creates a bot that has the minimum set of files and code to get started. The bot echoes back to the user whatever they type in. You can use this template to get started building conversation flow in your bot. - -## Form bot -To create a bot that collects input from a user via a guided conversation, choose the **Form** template. A form bot is designed to collect a specific set of information from the user. For example, a bot that is designed to obtain a user's sandwich order would need to collect information such as type of bread, choice of toppings, size of sandwich, and so on. - -Bots created with the Form template in the C# language use [FormFlow](dotnet/bot-builder-dotnet-formflow.md) to manage forms, and bots created with the Form template in the Node.js language use [waterfalls](nodejs/bot-builder-nodejs-dialog-waterfall.md) to manage forms. - -## Language Understanding bot -To create a bot that uses natural language models to understand user intent, choose the **Language understanding** template. This template leverages Language Understanding (LUIS) to provide natural language understanding. - -If a user sends a message such as "get news about virtual reality companies," your bot can use LUIS to interpret the meaning of the message. Using LUIS, you can quickly deploy an HTTP endpoint that will interpret user input in terms of the intention that it conveys (find news) and the key entities that are present (virtual reality companies). LUIS enables you to specify the set of intents and entities that are relevant to your application, and then guides you through the process of building a language understanding application. - -When you create a bot using the Language understanding template, Bot Service creates a corresponding LUIS application that is empty (i.e., that always returns `None`). To update your LUIS application model so that it is capable of interpreting user input, you must sign-in to LUIS, click **My applications**, select the application that the service created for you, and then create intents, specify entities, and train the application. - -## Question and Answer bot -To create a bot that distills semi-structured data like question and answer pairs into distinct, helpful answers, choose the **Question and Answer** template. This template leverages the QnA Maker service to parse questions and provide answers. - -When you create a bot with the Question and Answer template, you must sign-in to QnA Maker and create a new QnA service. This QnA service will give you the knowledge base ID and subscription key that you can use to update your [App Settings](bot-service-manage-settings.md) values for **QnAKnowldegebasedId** and **QnASubscriptionKey**. Once those values are set, your bot can answer any questions that the QnA service has in its knowledge base. - -## Proactive bot -To create a bot that can send proactive messages to the user, choose the **Proactive template**. Typically, each message that a bot sends to the user directly relates to the user's prior input. In some cases though, a bot may need to send the user information that is not directly related to the user's most recent message. These types of messages are called **proactive messages**. Proactive messages can be useful in a variety of scenarios. For example, if a bot sets a timer or reminder, it may need to notify the user when the time arrives. Or, if a bot receives a notification about an external event, it may need to communicate that information to the user. - -When you create a bot by using the Proactive template, several Azure resources are automatically created and added to your resource group. By default, these Azure resources are already configured to enable a very simple proactive messaging scenario. - -| Resource | Description | -|----|----| -| Azure Storage | Used to create the queue. | -| Azure Function App | A `queueTrigger` Azure Function that is triggered whenever there is a message in the queue. It communicates to Bot Service by using [Direct Line](https://docs.microsoft.com/bot-framework/rest-api/bot-framework-rest-direct-line-3-0-concepts). This function uses bot binding to send the message as part of the trigger’s payload. Our example function forwards the user’s message as-is from the queue. -| Bot Service | Your bot. Contains the logic that receives the message from user, adds the message to the Azure queue, receives triggers from Azure Function, and sends back the message it received via trigger's payload. | - -The following diagram shows how triggered events work when you create a bot using the Proactive template. - -![Overview of example Proactive Bot](~/media/bot-proactive-diagram.png) - -The process begins when the user sends a message to your bot via Bot Framework servers (step 1). - -## Next steps -Now that you know about the different templates, learn about Cognitive Services for use within bots. - -> [!div class="nextstepaction"] -> [Cognitive Services for bots](bot-service-concept-intelligence.md) diff --git a/articles/bot-service-debug-bot.md b/articles/bot-service-debug-bot.md index 7a5531931..3bfbec6e7 100644 --- a/articles/bot-service-debug-bot.md +++ b/articles/bot-service-debug-bot.md @@ -1,156 +1,134 @@ --- -title: Debug a bot - Bot Service -description: Learn how to debug a bot built using Bot Service. -author: v-ducvo -ms.author: kamrani +title: How to debug a Bot Framework SDK bot +description: Learn how to use Bot Framework Emulator to debug bots. See how to set breakpoints in IDEs and how to exchange messages with bots during debugging. keywords: Bot Framework SDK, debug bot, test bot, bot emulator, emulator -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/26/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: "azure-bot-service-4.0" +ms.custom: + - evergreen --- -# Debug a bot +# Debug an SDK-first bot -This article describes how to debug your bot using an integrated development environment (IDE) such as Visual Studio or Visual Studio Code and the Bot Framework Emulator. While you can use these methods to debug any bot locally, this article uses a [C# bot](~/dotnet/bot-builder-dotnet-sdk-quickstart.md) or [Javascript bot](~/javascript/bot-builder-javascript-quickstart.md) created in the quickstart. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -> [!NOTE] -> In this article, we use the Bot Framework Emulator to send and receive messages from the bot during debugging. If you are looking for other ways to debug your bot using the Bot Framework Emulator, please read the [Debug with the Bot Framework Emulator](https://docs.microsoft.com/azure/bot-service/bot-service-debug-emulator) article. - -## Prerequisites -- Download and install the [Bot Framework Emulator](https://aka.ms/Emulator-wiki-getting-started). -- Download and install [Visual Studio Code](https://code.visualstudio.com) or [Visual Studio](https://www.visualstudio.com/downloads) (Community Edition or above). - - +## Prerequisites -## Debug a JavaScript bot using breakpoints in Visual Studio Code +- Download and install the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md). +- Download and install [Visual Studio Code](https://code.visualstudio.com) or [Visual Studio](https://www.visualstudio.com/downloads). -In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. To set breakpoints in VS Code, do the following: +## [C#](#tab/csharp) -1. Launch VS Code and open your bot project folder. -2. From the menu bar, click **Debug** and click **Start Debugging**. If you are prompted to select a runtime engine to run your code, select **Node.js**. At this point, the bot is running locally. -3. Click the **.js** file and set breakpoints as necessary. In VS Code, you can set breakpoints by hovering your mouse over the column to the left of the line numbers. A small red dot will appear. If you click on the dot, the breakpoint is set. If you click the dot again, the breakpoint is removed. +### Set C# breakpoints in Visual Studio Code - ![Set breakpoint in VS Code](~/media/bot-service-debug-bot/breakpoint-set.png) +In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. To set breakpoints in Visual Studio Code, do the following: -4. Start the Bot Framework Emulator and connect to your bot as described in the [Debug with the Bot Framework Emulator](https://docs.microsoft.com/azure/bot-service/bot-service-debug-emulator) article. -5. From the emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. +1. Launch Visual Studio Code and open your bot project folder. +1. Set breakpoints as necessary. To set a breakpoint, hover your mouse over the column to the left of the line numbers. A small red dot will appear. If you select the dot, the breakpoint is set. If you select the dot again, the breakpoint is removed. - ![Debug in VS Code](~/media/bot-service-debug-bot/breakpoint-caught.png) + :::image type="content" source="media/bot-service-debug-bot/csharp-breakpoint-set.png" alt-text="A screenshot of a C# breakpoint set in Visual Studio Code."::: -## Debug a C# bot using breakpoints in Visual Studio +1. From the menu bar, select **Run**, then **Start Debugging**. Your bot will start running in debugging mode from the Terminal in Visual Studio Code. +1. Start the Bot Framework Emulator and connect to your bot as described in how to [Debug with the Bot Framework Emulator](bot-service-debug-emulator.md). +1. From the Emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. -In Visual Studio (VS), you can set breakpoints and run the bot in debug mode to step through your code. To set breakpoints in VS, do the following: + :::image type="content" source="media/bot-service-debug-bot/breakpoint-caught.png" alt-text="A screenshot of a C# bot in Visual Studio Code, paused at a break point."::: -1. Navigate to your bot folder and open the **.sln** file. This will open the solution in VS. -2. From the menu bar, click **Build** and click **Build Solution**. -3. In the **Solution Explorer**, click the **.cs** file and set breakpoints as necessary. This file defines your main bot logic. In VS, you can set breakpoints by hovering your mouse over the column to the left of the line numbers. A small red dot will appear. If you click on the dot the breakpoint is set. If you click the dot again the breakpoint is removed. -4. From the menu, click **Debug** and click **Start Debugging**. At this point, the bot is running locally. +### Set C# breakpoints in Visual Studio - ![Set breakpoint in VS](~/media/bot-service-debug-bot/breakpoint-set-vs.png) +In Visual Studio, you can set breakpoints and run the bot in debug mode to step through your code. To set breakpoints in Visual Studio, do the following: - +1. Navigate to your bot folder and open the **.sln** file. This will open the solution in Visual Studio. +1. From the menu bar, select **Build** and select **Build Solution**. +1. In the **Solution Explorer**, select the **.cs** file and set breakpoints as necessary. This file defines your main bot logic. To set a breakpoint, hover your mouse over the column to the left of the line numbers. A small dot will appear. If you select the dot, the breakpoint is set. If you select the dot again, the breakpoint is removed. -5. Start the Bot Framework Emulator and connect to your bot as described in the section above. -6. From the emulator, send your bot a message (e.g.: send the message "Hi"). Execution will stop at the line where you place the breakpoint. + :::image type="content" source="media/bot-service-debug-bot/breakpoint-set-vs.png" alt-text="A screenshot of a C# breakpoint set in Visual Studio."::: - ![Debug in VS](~/media/bot-service-debug-bot/breakpoint-caught-vs.png) +1. From the menu, select **Debug**, then **Start Debugging**. At this point, the bot is running locally. +1. Start the Bot Framework Emulator and connect to your bot as described in the section above. +1. From the Emulator, send your bot a message, such as "Hi". Execution will stop at the line where you place the breakpoint. -::: moniker range="azure-bot-service-3.0" + :::image type="content" source="media/bot-service-debug-bot/breakpoint-caught-vs.png" alt-text="A screenshot of a C# bot in Visual Studio, paused at a break point."::: -## Debug a Consumption plan C\# Functions bot +## [JavaScript](#tab/javascript) -The Consumption plan serverless C\# environment in Bot Service has more in common with Node.js than a typical C\# application because it requires a runtime host, much like the Node engine. In Azure, the runtime is part of the hosting environment in the cloud, but you must replicate that environment locally on your desktop. +### Set JavaScript breakpoints in Visual Studio Code -### Prerequisites +In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. To set breakpoints in Visual Studio Code, do the following: -Before you can debug your Consumption plan C# bot, you must complete these tasks. +1. Launch Visual Studio Code and open your bot project folder. +1. From the menu bar, select **Debug** and then select **Start Debugging**. If you're prompted to select a runtime engine to run your code, select **Node.js**. At this point, the bot is running locally. +1. Select the **.js** file and set breakpoints as necessary. To set a breakpoint, hover your mouse over the column to the left of the line numbers. A small red dot will appear. If you select the dot, the breakpoint is set. If you select the dot again, the breakpoint is removed. -- Download the source code for your bot (from Azure), as described in [Set up continuous deployment](bot-service-continuous-deployment.md). -- Download and install the [Bot Framework Emulator](https://aka.ms/Emulator-wiki-getting-started). -- Install the Azure Functions CLI. -- Install the DotNet CLI. - -If you want to be able to debug your code by using breakpoints in Visual Studio 2017, you must also complete these tasks. - -- Download and install Visual Studio 2017 (Community Edition or above). -- Download and install the Command Task Runner Visual Studio Extension. + :::image type="content" source="media/bot-service-debug-bot/breakpoint-set.png" alt-text="A screenshot of a JavaScript breakpoint set in Visual Studio Code."::: -> [!NOTE] -> Visual Studio Code is not currently supported. +1. Start the Bot Framework Emulator and connect to your bot as described in the [Debug with the Bot Framework Emulator](bot-service-debug-emulator.md) article. +1. From the Emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. -### Debug a Consumption plan C# Functions bot using the emulator + :::image type="content" source="media/bot-service-debug-bot/breakpoint-caught.png" alt-text="A screenshot of a JavaScript bot in Visual Studio Code, paused at a break point."::: -The simplest way to debug your bot locally is to start the bot and then connect to it from Bot Framework Emulator. -First, open a command prompt and navigate to the folder where the **project.json** file is located in your repository. Then, run the command `dotnet restore` to restore the various packages that are referenced in your bot. +## [Java](#tab/java) -> [!NOTE] -> Visual Studio 2017 changes how Visual Studio handles dependencies. -> While Visual Studio 2015 uses **project.json** to handle dependencies, -> Visual Studio 2017 uses a **.csproj** model when loading in Visual Studio. -> If you are using Visual Studio 2017, download this **.csproj** file -> to the **/messages** folder in your repository before you run the `dotnet restore` command. +### Set Java breakpoints in Visual Studio Code -![Command prompt](~/media/bot-service-debug-bot/csharp-azureservice-debug-envconfig.png) +In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. See also [Create a bot with the Bot Framework SDK for Java](~/java/bot-builder-java-quickstart.md). -Next, run `debughost.cmd` to load and start your bot. +1. Install the [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack) in Visual Studio Code, if you haven't already done so. This extension provides rich support for Java in Visual Studio Code, including debugging. +1. Launch Visual Studio Code and open your bot project folder. +1. Set breakpoints as necessary. To set a breakpoint, hover your mouse over the column to the left of the line numbers. A small red dot will appear. If you select the dot, the breakpoint is set. If you select the dot again, the breakpoint is removed. -![Command prompt run debughost.cmd](~/media/bot-service-debug-bot/csharp-azureservice-debug-debughost.png) + :::image type="content" source="media/bot-service-debug-bot/bot-debug-java-breakpoints.png" alt-text="A screenshot of a Java breakpoint set in Visual Studio Code."::: -At this point, the bot is running locally. From the console window, copy the endpoint that debughost is listening on (in this example, `http://localhost:3978`). Then, start the Bot Framework Emulator and paste the endpoint into the address bar of the emulator. For this example, you must also append `/api/messages` to the endpoint. Since you do not need security for local debugging, you can leave the **Microsoft App ID** and **Microsoft App Password** fields blank. Click **Connect** to establish a connection to your bot using the specified endpoint. +1. Select the `EchoBot.java` file and add a breakpoint to a desired location. +1. From the menu bar, select **Run** and then select **Start Debugging**. +1. Select **Java** if prompted to debug the currently selected file. +1. Start the Bot Framework Emulator and connect to your bot as described in the [Debug with the Bot Framework Emulator](bot-service-debug-emulator.md) article. +1. From the Emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. -![Configure emulator](~/media/bot-service-debug-bot/mac-azureservice-emulator-config.png) + :::image type="content" source="media/bot-service-debug-bot/bot-debug-java-breakpoint-caught.png" alt-text="A screenshot of a Java bot in Visual Studio Code, paused at a break point."::: -After you have connected the emulator to your bot, send a message to your bot by typing some text into the textbox that is located at the bottom of the emulator window (i.e., where **Type your message...** appears in the lower-left corner). By using the **Log** and **Inspector** panels on the right side of the emulator window, you can view the requests and responses as messages are exchanged between the emulator and the bot. +For more information, see [Running and debugging Java](https://code.visualstudio.com/docs/java/java-debugging). -![test via emulator](~/media/bot-service-debug-bot/mac-azureservice-debug-emulator.png) +## [Python](#tab/python) -Additionally, you can view log details in the console window. +### Set Python breakpoints in Visual Studio Code -![Console window](~/media/bot-service-debug-bot/csharp-azureservice-debug-debughostlogging.png) - -::: moniker-end +In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. See also [Create a bot with the Bot Framework SDK for Python](~/python/bot-builder-python-quickstart.md). -## Debug a Python bot using breakpoints in Visual Studio Code +1. Install the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) in Visual Studio Code, if you haven't already done so. This extension provides rich support for Python in Visual Studio Code, including debugging. +1. Launch Visual Studio Code and open your bot project folder. +1. Set breakpoints as necessary. To set a breakpoint, hover your mouse over the column to the left of the line numbers. A small red dot will appear. If you select the dot, the breakpoint is set. If you select the dot again, the breakpoint is removed. -In Visual Studio Code, you can set breakpoints and run the bot in debug mode to step through your code. See also [Create a bot with the Bot Framework SDK for Python](~/python/bot-builder-python-quickstart.md). + :::image type="content" source="media/bot-service-debug-bot/bot-debug-python-breakpoints.png" alt-text="A screenshot of a Python breakpoint set in Visual Studio Code."::: -1. Launch VS Code and open your bot project folder. -1. Set breakpoints as necessary. You can set breakpoints by hovering your mouse over the column to the left of the line numbers. A small red dot will appear. If you click on the dot, the breakpoint is set. If you click the dot again, the breakpoint is removed. -1. Select the `app.py`. -1. From the menu bar, click **Debug** and click **Start Debugging**. +1. Select the `app.py` file. +1. From the menu bar, select **Debug** and then select **Start Debugging**. 1. Select **Python File** to debug the currently selected file. +1. Start the Bot Framework Emulator and connect to your bot as described in the [Debug with the Bot Framework Emulator](bot-service-debug-emulator.md) article. +1. From the Emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. - ![Set breakpoints](~/media/bot-service-debug-bot/bot-debug-python-breakpoints.png) + :::image type="content" source="media/bot-service-debug-bot/bot-debug-python-breakpoint-caught.png" alt-text="A screenshot of a Python bot in Visual Studio Code, paused at a break point."::: -1. Start the Bot Framework Emulator and connect to your bot as described in the [Debug with the Bot Framework Emulator](https://docs.microsoft.com/azure/bot-service/bot-service-debug-emulator) article. -1. From the emulator, send your bot a message (for example, send the message "Hi"). Execution will stop at the line where you place the breakpoint. +For more information, see [Debug your Python code](/visualstudio/python/debugging-python-in-visual-studio). - ![Debug in VS Code](~/media/bot-service-debug-bot/bot-debug-python-breakpoint-caught.png) - -For more information, see [Debug your Python code](https://aka.ms/bot-debug-python). +--- ## Additional resources -- See [troubleshoot general problems](bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. -- See the how to [Debug with the Emulator](bot-service-debug-emulator.md). +- [Troubleshoot bot configuration issues](bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. +- [Debug with the Emulator](bot-service-debug-emulator.md). ## Next steps diff --git a/articles/bot-service-debug-channel-devtunnel.md b/articles/bot-service-debug-channel-devtunnel.md new file mode 100644 index 000000000..9ee1269c8 --- /dev/null +++ b/articles/bot-service-debug-channel-devtunnel.md @@ -0,0 +1,79 @@ +--- +title: Debug a channel using a tunnel +description: Understand how to debug a channel using a tunnel +keywords: debugging, channel, tunnel +author: jameslew +ms.author: jameslew +manager: kjette +ms.reviewer: cyanderson +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Debug a bot from any channel using a tunnel + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +While your bot is in development, you can use an IDE and the Bot Framework Emulator to chat with your bot locally and inspect the messages your bot sends and receives. +If your bot is in production, you can debug your bot from any channel using a tunnel. The seamless connection of your bot to multiple channels is a key feature available in the Bot Framework. + +This article describes how to debug your bot locally using a tunnel and a C# [EchoBot](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/02.echo-bot) in a channel connected to your bot. This article uses [Microsoft Teams](channel-connect-teams.md) as an example channel. + + +## Prerequisites + +- A subscription to [Microsoft Azure](https://azure.microsoft.com/). +- Install a tunneling software such as [Dev Tunnels](https://aka.ms/devtunnels). +- A C# [Echo bot](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/02.echo-bot), configured as a multi-tenant app, and connected to any [channel](bot-service-manage-channels.md). + +## Configure a tunnel + +[Dev Tunnels](https://aka.ms/devtunnels) is a cross-platform application that can create a tunneling or forwarding URL, so that internet requests reach your local machine. Use devtunnel to forward messages from external channels on the web directly to your local machine to allow debugging, as opposed to the standard messaging endpoint configured in the Azure portal. + +1. Open a terminal with access to `devtunnel` CLI. + +1. Run devtunnel with the following command to create a new tunnel. + + ```console + devtunnel host -a -p 3978 + ``` + + > [!NOTE] + > The port specified is the port your bot is running on. You may use any localhost port you'd like. + + +1. When devtunnel starts, copy and save the public forwarding URL for later. + + :::image type="content" source="media/debug-devtunnel/devtunnel-forwarding-url.png" alt-text="devtunnel forwarding URL"::: + +## Configure in Azure portal + +While devtunnel is running, sign in to your Azure portal and view your bot settings to do some configuration. + +1. Select your bot resource connected to your local bot. + +1. Locate **Settings/Configuration**. Copy and paste the devtunnel forwarding URL in the **Messaging endpoint** field. Ensure that you maintain "/api/messages" at the end of the URL. + + :::image type="content" source="media/debug-devtunnel/messaging-endpoint.png" alt-text="Messaging endpoint"::: + +1. Select **Apply**. + +## Test + +At this point, incoming messages from to your bot from external channels will now be sent to your local bot. The sample bot we'll use to demonstrate this is already configured live for **Microsoft Teams**. Read [Connect a bot to Microsoft Teams](channel-connect-teams.md) about connecting a local bot with **Microsoft Teams** channel. + +Locally, you can set breakpoints in Visual Studio. Expanding the text property from the incoming activity object, you'll see that the message you sent the bot from teams is being intercepted locally for you to debug. + +:::image type="content" source="media/debug-devtunnel/breakpoint.png" alt-text="Set breakpoints"::: + +From here, you can debug normally, and run your code step by step. Use this to debug your bot from any channel. + +## Additional information + +- [Connect a bot to channels](bot-service-manage-channels.md) +- [Debug your bot locally using an IDE](bot-service-debug-bot.md) +- [Debug a bot using the Bot Framework Emulator](bot-service-debug-emulator.md) +- [Debug a bot with inspection middleware](bot-service-debug-inspection-middleware.md) diff --git a/articles/bot-service-debug-cortana-skill.md b/articles/bot-service-debug-cortana-skill.md deleted file mode 100644 index 9116e9928..000000000 --- a/articles/bot-service-debug-cortana-skill.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Test a Cortana skill - Bot Service -description: Learn how to test a Cortana bot by invoking a Cortana skill. -keywords: Bot Framework SDK, register your bot, cortana -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/01/2018 -monikerRange: 'azure-bot-service-3.0' ---- - -# Test a Cortana skill - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -If you've built a Cortana skill using the Bot Framework SDK, you can test it by invoking it from Cortana. The following instructions walk you through the steps required to try out your Cortana skill. - -## Register your bot -If you [created your bot](~/bot-service-quickstart.md) using Bot Service in Azure, then your bot is already registered and you can skip this step. - -If you have deployed your bot elsewhere or if you want to test your bot locally, then you must [register](bot-service-quickstart-registration.md) your bot so that you can connect it to Cortana. During the registration process, you will need to provide your bot's **Messaging endpoint**. If you choose to test your bot locally, you will need to run a tunneling software such as [ngrok](http://ngrok.com) to get an endpoint for your local bot. - -## Get messaging endpoint using ngrok - -If you are running the bot locally, you can get an endpoint to use for testing by running tunneling software, such as [ngrok](https://ngrok.com). To use ngrok to get an endpoint, from a console window type: - -```cmd - ngrok.exe http 3979 -host-header="localhost:3979" -``` - -This configures and displays an ngrok forwarding link that forwards requests to your bot, which is running on port 3978. The URL to the forwarding link should look something like this: `https://0d6c4024.ngrok.io`. Append `/api/messages` to the link, to form a messaging endpoint URL in this format: `https://0d6c4024.ngrok.io/api/messages`. - -Enter this endpoint URL in the **Configuration** section of your bot's [Settings](~/bot-service-manage-settings.md) blade. - -## Enable speech recognition priming -If your bot uses a Language Understanding (LUIS) app, make sure you associate the LUIS application ID with your registered bot service. This helps your bot recognize spoken utterances that are defined in your LUIS model. For more information, see [Speech priming](~/bot-service-manage-speech-priming.md). - -## Add the Cortana channel -To add Cortana as a channel, follow steps listed in [Connect a bot to Cortana](bot-service-channel-connect-cortana.md). - -## Test using Web Chat control - -To test your bot using the integrated web chat control in Bot Service, click **Test in Web Chat** and type a message to verify that your bot is working. - -## Test using emulator - -To test your bot using the [emulator](~/bot-service-debug-emulator.md), do the following: - -1. Run the bot. -2. Open the emulator and fill in the necessary information. To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). -3. Click **Connect** to connect the emulator to your bot. -4. Type a message to verify that your bot is working. - -## Test using Cortana -You can invoke your Cortana skill by speaking an invocation phrase to Cortana. -1. Open Cortana. -2. Open the Notebook within Cortana and click **About me** to see which account you're using for Cortana. Make sure you are signed in with the same Microsoft account that you used to register your bot. - ![Sign in to Cortana's notebook](~/media/cortana/cortana-notebook.png) -2. Click on the microphone button in the Cortana app or in the "Ask me anything" search box in Windows, and say your bot's [invocation phrase][InvocationNameGuidelines]. The invocation phrase includes an *invocation name*, which uniquely identifies the skill to invoke. For example, if a skill's invocation name is "Northwind Photo", a proper invocation phrase could include "Ask Northwind Photo to..." or "Tell Northwind Photo that...". - - You specify your bot's *Invocation Name* when you configure it for Cortana. - ![Enter the invocation name when you configure the Cortana channel](~/media/cortana/cortana-invocation-name-callout.png) - -3. If Cortana recognizes your invocation phrase, your bot launches in Cortana's canvas. - -## Troubleshoot - -If your Cortana skill fails to launch, check the following: -* Make sure you are signed in to Cortana using the same Microsoft account that you used to register your bot in the Bot Framework Portal. -* Check if the bot is working by clicking **Test in Web chat** to open the **Chat** window and typing a message to it. -* Check if your invocation name meets the [guidelines][InvocationNameGuidelines]. If your invocation name is longer than three words, hard to pronounce, or sounds like other words, Cortana might have difficulty recognizing it. -* If your skill uses a LUIS model, make sure you [enable speech recognition priming](~/bot-service-manage-speech-priming.md). - -See the [Enable Debugging of Cortana skills][Cortana-TestBestPractice] for additional troubleshooting tips and information on how to enable debugging of your skill in the Cortana dashboard. - - -## Next steps - -Once you have tested your Cortana skill and verified that it works the way you'd like it to, you can deploy it to a group of beta testers or release it to the public. See [Publishing Cortana Skills][Cortana-Publish] for more information. - -## Additional resources -* [The Cortana Skills Kit][CortanaGetStarted] - -[CortanaGetStarted]: /cortana/getstarted - -[BFPortal]: https://dev.botframework.com/ -[CortanaDevCenter]: https://developer.microsoft.com/cortana - -[CortanaSpecificEntities]: https://aka.ms/cortana-channel-data -[CortanaAuth]: https://aka.ms/add-auth-cortana-skill - -[InvocationNameGuidelines]: https://aka.ms/cortana-invocation-guidelines - - -[Cortana-Debug]: https://aka.ms/cortana-enable-debug -[Cortana-TestBestPractice]: https://aka.ms/cortana-test-best-practice -[Cortana-Publish]: /cortana/skills/publish-skill diff --git a/articles/bot-service-debug-emulator.md b/articles/bot-service-debug-emulator.md index 7d402f332..b6b7fb9a7 100644 --- a/articles/bot-service-debug-emulator.md +++ b/articles/bot-service-debug-emulator.md @@ -1,211 +1,206 @@ --- -title: Test and debug bots using the Bot Framework Emulator - Bot Service +title: Test and debug bots using the Bot Framework Emulator description: Learn how to inspect, test, and debug bots using the Bot Framework Emulator desktop application. keywords: transcript, msbot tool, language services, speech recognition -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/26/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Debug with the emulator +# Test and debug with the Emulator -The Bot Framework Emulator is a desktop application that allows bot developers to test and debug their bots, either locally or remotely. Using the emulator, you can chat with your bot and inspect the messages that your bot sends and receives. The emulator displays messages as they would appear in a web chat UI and logs JSON requests and responses as you exchange messages with your bot. Before you deploy your bot to the cloud, run it locally and test it using the emulator. You can test your bot using the emulator even if you have not yet [created](./bot-service-quickstart.md) it with Azure Bot Service or configured it to run on any channels. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Bot Framework Emulator is a desktop application that allows bot developers to test and debug bots, either locally or remotely. Using the Emulator, you can chat with your bot and inspect the messages that your bot sends and receives. The Emulator displays messages as they would appear in a web chat UI and logs JSON requests and responses as you exchange messages with your bot. Before you deploy your bot to the cloud, run it locally and test it using the Emulator. You can test your bot using the Emulator even if you haven't yet [created](./bot-service-quickstart.md) it with Azure AI Bot Service or configured it to run on any channels. + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] ## Prerequisites -- Install [Bot Framework Emulator](https://aka.ms/Emulator-wiki-getting-started) + +- Install [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md) ## Run a bot locally -Before connecting your bot to the Bot Framework Emulator, you need to run your bot locally. You can use Visual Studio or Visual Studio Code to run your bot, or use command line. + +Before connecting your bot to the Bot Framework Emulator, you need to run your bot locally. You can use Visual Studio or Visual Studio Code to run your bot, or use command line. To run a bot using command line, do the following: +### [C#](#tab/csharp) -# [C#](#tab/csharp) +- Go to the command prompt and change directory to your bot project directory. +- Start the bot by running the following command: -* Go to the command prompt and change directory to your bot project directory. -* Start the bot by running the following command: - ``` + ```cmd dotnet run ``` -* Copy the port number in the line before *Application started. Press CTRL+C to shut down.* - ![C# Port Number](media/bot-service-debug-emulator/csharp_port_number.png) +- Copy the port number in the line before *Application started. Press CTRL+C to shut down.* + :::image type="content" source="media/bot-service-debug-emulator/csharp_port_number.png" alt-text="C# port number"::: -# [JavaScript](#tab/javascript) +### [JavaScript](#tab/javascript) -* Go to the command prompt and change directory to your bot project directory. -* Start the bot by running the following command: - ``` +- Go to the command prompt and change directory to your bot project directory. +- Start the bot by running the following command: + + ```cmd node index.js ``` -* Copy the port number that restify is listening on. - ![JS Port Number](media/bot-service-debug-emulator/js_port_number.png) +- Copy the port number that restify is listening on. -# [Python](#tab/python) -* Go to the command prompt and change directory to your bot project directory. -* Start the bot by running the following command: - ``` - python app.py - ``` -* Copy the port number that restify is listening on. + :::image type="content" source="media/bot-service-debug-emulator/js_port_number.png" alt-text="JavaScript port number"::: - ![JS Port Number](media/bot-service-debug-emulator/js_port_number.png) +### [Java](#tab/java) ---- +- Go to the command prompt and change directory to your bot project directory. -At this point, your bot should be running locally. +- If this is the first time, build the bot running the following command: + ```cmd + mvn package + ``` -## Connect to a bot running on localhost +- Start the bot by running the following command: - -### Configure the emulator for authentication + ```cmd + java -jar .\target\.jar + ``` -If a bot requires authentication, displaying a login dialog, you must configure the emulator as shown below. +- Copy the port number that Tomcat web server is listening on. -#### Using sign-in verification code + :::image type="content" source="media/bot-service-debug-emulator/java_port_number.png" alt-text="Java port number"::: -1. Start the emulator. -1. In the emulator, click the gear icon in the bottom left, or the **Emulator Settings** tab in the upper right. -1. Check the box by **Use a sign-in verification code for OAuthCards**. -1. Check the box by **Bypass ngrok for local address** -1. Click the **Save** button. +### [Python](#tab/python) -When you click the login button displayed by the bot, a validation code will be generated. -You wil enter the code in the bot input chat box for the authentication to take place. -After that you can perform the allowed operations. +- Go to the command prompt and change directory to your bot project directory. +- Start the bot by running the following command: -Alternatively, you can perform the steps described below. + ```cmd + python app.py + ``` -#### Using authentication tokens +- Copy the port number that restify is listening on. -1. Start the emulator. -1. In the emulator, click the gear icon in the bottom left, or the **Emulator Settings** tab in the upper right. -1. Check the box by **Use version1.0 authentication tokens**. -1. Enter the local path to the **ngrok** tool. For more the tool information, see [ngrok](https://ngrok.com/). -1. Check the box by **Run ngrok when the Emulator starts up**. -1. Click the **Save** button. + :::image type="content" source="media/bot-service-debug-emulator/python_port_number.png" alt-text="Python port number"::: -When you click the login button displayed by the bot, you will be asked to enter your credentials. An authentication token is generated. After that you can perform the allowed operations. +--- +At this point, your bot should be running locally. -![Emulator UI](media/emulator-v4/emulator-welcome.png) +## Connect to a bot running on localhost -To connect to a bot running locally and click **Open bot**. Add the port number your copied earlier into the following URL and paste the updated URL in the Bot URL bar: +### Configure proxy settings -*http://localhost:**port number**/api/messages* +When you're developing behind a corporate proxy, the Emulator will use the configured environment variables `HTTP_PROXY` and `HTTPS_PROXY`, which specify the proxy URL route for HTTP and HTTPs requests respectively. -![Emulator UI](media/bot-service-debug-emulator/open_bot_emulator.png) +If you're connecting to a bot running on `localhost`, the Emulator will first try to route through the proxy before connecting to `localhost`. Typically, the proxy will block the connection unless you specify that it should be bypassed for `localhost`. -If your bot is running with [Microsoft Account (MSA) credentials](#use-bot-credentials), enter these credentials too. +In order to bypass the `HTTP_PROXY` and `HTTPS_PROXY` settings and allow the Emulator to connect to `localhost`, on your local machine you must define the following environment variable: -### Use bot credentials +```cmd +NO_PROXY=localhost +``` -When you open the bot, set the **Microsoft App ID** and **Microsoft App password** if your bot is running with credentials. If you created your bot with the Azure Bot Service, the credentials are available on the bot's App Service, under the **Settings -> Configuration** section. If you do not know the values, you can remove those from the locally running bot's configuration file, then run the bot in the Emulator. If the bot isn't running with these settings, you don't need to run the emulator with the settings either. +### Configure the Emulator for authentication -When creating an AD identity provider application, remember the following: +If a bot requires authentication, displaying a login dialog, you must configure the Emulator as shown below. + +#### Using a sign-in verification code -- When the supported account types is set to single tenant, if you use a personal subscription instead of a Microsoft account, the emulator would issue the error: *The bot's Microsoft App ID or Microsoft App Password is incorrect..* -- In this case, the supported account types must be set to *Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)*. +1. Start the Emulator. +1. In the Emulator, select **Settings** (the gear icon) in the left pane. +1. Enable **Use a sign-in verification code for OAuthCards**. +1. Select **Save**. -For more information, see [Create an Azure AD identity provider application](bot-builder-tutorial-authentication.md#create-an-azure-ad-identity-provider-application). +When you select the login button displayed by the bot, a validation code will be generated. +You'll enter the code in the bot input chat box for the authentication to take place. +After that you can perform the allowed operations. -## View detailed Message Activity with the Inspector +Alternatively, you can perform the steps described below. -Send a message to your bot and the bot should respond back. You can click on the message bubble within the conversation window and inspect the raw JSON activity using the **INSPECTOR** feature to the right side of the window. When selected, the message bubble will turn yellow and the activity JSON object will be displayed to the left of the chat window. The JSON information includes key metadata, including the channel ID, activity type, conversation ID, the text message, endpoint URL, and so on. You can inspect activities sent from the user, as well as activities the bot responds with. +#### Using authentication tokens -![Emulator Message Activity](media/emulator-v4/emulator-view-message-activity-03.png) +1. Start the Emulator. +1. In the Emulator, select **Settings** (the gear icon) in the left pane. +1. Enable **Use version 1.0 authentication tokens**. +1. Select **Save**. -> [!TIP] -> You can debug state changes in a bot connected to a channel by adding [Inspection Middleware](bot-service-debug-inspection-middleware.md) to the bot. +When you select the login button displayed by the bot, you'll be asked to enter your credentials. An authentication token is generated. After that you can perform the allowed operations. - - +> [!TIP] +> You can debug state changes in a bot connected to a channel by adding [Inspection Middleware](bot-service-debug-inspection-middleware.md) to the bot. ## Inspect services -With the new v4 emulator you can also inspect the JSON responses from LUIS and QnA. Using a bot with a connected language service, you can select **trace** in the LOG window to the bottom right. This new tool also provides features to update your language services directly from the emulator. +[!INCLUDE [qnamaker-sunset-alert](includes/qnamaker-sunset-alert.md)] + +[!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] -![LUIS Inspector](media/emulator-v4/emulator-luis-inspector.png) +With the Emulator, you can also inspect the JSON responses from LUIS and QnA Maker. Using a bot with a connected language service, you can select **trace** in the LOG window to the bottom right. This new tool also provides features to update your language services directly from the Emulator. -With a connected LUIS service, you'll notice that the trace link specifies **Luis Trace**. When selected, you'll see the raw response from your LUIS service, which includes intents, entities along with their specified scores. You also have the option to re-assign intents for your user utterances. +:::image type="content" source="media/emulator-v4/emulator-luis-inspector.png" alt-text="LUIS Inspector"::: -![QnA Inspector](media/emulator-v4/emulator-qna-inspector.png) +With a connected LUIS service, the trace link specifies **Luis Trace**. When selected, the raw response from your LUIS service is displayed, which includes intents and entities, along with their specified scores. You can reassign intents for your user utterances. -With a connected QnA service, the log will display **QnA Trace**, and when selected you can preview the question and answer pair associated with that activity, along with a confidence score. From here, you can add alternative question phrasing for an answer. +:::image type="content" source="media/emulator-v4/emulator-qna-inspector.png" alt-text="QnA Inspector"::: - +1. Select **File**, then **Sign in with Azure**. - +1. On the **Welcome** screen, select **Sign in with your Azure account**. You can optionally have Emulator keep you signed in across Emulator application restarts. -### Login to Azure -You can use Emulator to login in to your Azure account. This is particularly helpful for you to add and manage services your bot depends on. Log into Azure by following these steps: -- Click on File -> Sign in with Azure -![Azure login](media/emulator-v4/emulator-azure-login.png) -- On the welcome screen click on Sign in with your Azure account -You can optionally have Emulator keep you signed in across Emulator application restarts. -![Azure login](media/emulator-v4/emulator-azure-login-success.png) + :::image type="content" source="media/emulator-v4/emulator-azure-login-success.png" alt-text="Emulator Azure sign-in success"::: ## Disabling data collection If you decide that you no longer want to allow the Emulator to collect usage data, you can easily disable data collection by following these steps: -1. Navigate to the Emulator's settings page by clicking on the Settings button (gear icon) in the nav bar on the left side. - - ![disable data collection](media/emulator-v4/emulator-disable-data-1.png) +1. In the Emulator, select **Settings** (the gear icon) in the left pane. -2. Uncheck the checkbox labeled *Help improve the Emulator by allowing us to collect usage data* under the **Data Collection** section. + :::image type="content" source="media/emulator-v4/emulator-disable-data-1.png" alt-text="Emulator settings button"::: - ![disable data collection](media/emulator-v4/emulator-disable-data-2.png) +1. Under **Data Collection**, deselect **Help improve the Emulator by allowing us to collect usage data**. +1. Select **Save**. -3. Click the "Save" button. - - ![disable data collection](media/emulator-v4/emulator-disable-data-3.png) - -If you change your mind, you can always enable it by re-checking the checkbox. +If you change your mind, you can reenable data collection later. ## Additional resources @@ -213,24 +208,12 @@ The Bot Framework Emulator is open source. You can [contribute][EmulatorGithubCo For troubleshooting, see [troubleshoot general problems](bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. -## Next steps +[EmulatorGithubContribute]: https://github.com/Microsoft/BotFramework-Emulator/wiki/How-to-Contribute +[EmulatorGithubBugs]: https://github.com/Microsoft/BotFramework-Emulator/wiki/Submitting-Bugs-%26-Suggestions + +## Next step Use inspection middleware to debug a bot connected to a channel. > [!div class="nextstepaction"] > [Debug your bot using transcript files](bot-service-debug-inspection-middleware.md) - - - - - -[EmulatorGithubContribute]: https://github.com/Microsoft/BotFramework-Emulator/wiki/How-to-Contribute -[EmulatorGithubBugs]: https://github.com/Microsoft/BotFramework-Emulator/wiki/Submitting-Bugs-%26-Suggestions - -[ngrokDownload]: https://ngrok.com/ -[inconshreveable]: https://inconshreveable.com/ diff --git a/articles/bot-service-debug-inspection-middleware.md b/articles/bot-service-debug-inspection-middleware.md index b92d0f045..272fafcad 100644 --- a/articles/bot-service-debug-inspection-middleware.md +++ b/articles/bot-service-debug-inspection-middleware.md @@ -1,165 +1,229 @@ --- -title: Debug a bot with inspection middleware - Bot Service -description: Learn how to debug a bot with inspection middleware -author: zxyanliu -ms.author: v-liyan -keywords: Bot Framework SDK, debug bot, inspection middleware, bot emulator, Azure Bot Channels Registration -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/01/2019 +title: Debug a bot with inspection middleware in the Bot Framework SDK +description: Learn how to use inspection middleware to debug bots. See how to use the Bot Framework Emulator to inspect state data and message traffic. +keywords: Bot Framework SDK, debug bot, inspection middleware, bot emulator, Azure Bot +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- # Debug a bot with inspection middleware -This article describes how to debug your bot using inspection middleware. This feature allows the Bot Framework Emulator to debug traffic into and out of the bot in addition to looking at the current state of the bot. You can use a trace message to send data to the emulator and then inspect the state of your bot in any given turn of the conversation. -We use an EchoBot built locally using the Bot Framework v4 ([C#](https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-sdk-quickstart?view=azure-bot-service-4.0) | [JavaScript](https://docs.microsoft.com/azure/bot-service/javascript/bot-builder-javascript-quickstart?view=azure-bot-service-4.0) | [Python](https://docs.microsoft.com/azure/bot-service/python/bot-builder-python-quickstart?view=azure-bot-service-4.0)) to show how to debug and inspect the bot's message state. You can also [Debug a bot using IDE](./bot-service-debug-bot.md) or [Debug with the Bot Framework Emulator](./bot-service-debug-emulator.md), but to debug state you need to add inspection middleware to your bot. The Inspection bot samples are available here: [C#](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/47.inspection), [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection) and [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/47.inspection). +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article describes how to debug a bot using inspection middleware. This feature allows the Bot Framework Emulator to debug traffic into and out of the bot, and to see the current state of the bot. You can use a trace message to send data to the Emulator and then inspect the state of your bot in any given turn of the conversation. + +We use an EchoBot built locally using the Bot Framework v4 in the [Create a bot quickstart](bot-service-quickstart-create-bot.md) to show how to debug and inspect the bot's message state. You can also [Debug a bot using IDE](./bot-service-debug-bot.md) or [Debug with the Bot Framework Emulator](./bot-service-debug-emulator.md), but to debug state you need to add inspection middleware to your bot. The inspection bot samples are available for [C#](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/47.inspection), [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/47.inspection), and [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/47.inspection). + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] ## Prerequisites -- Download and install the [Bot Framework Emulator](https://aka.ms/Emulator-wiki-getting-started) -- Knowledge of bot [Middleware](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-middleware?view=azure-bot-service-4.0) -- knowledge of bot [Managing state](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-state?view=azure-bot-service-4.0) -- Download and install [ngrok](https://ngrok.com/) (if you want to debug a bot configured in Azure to use additional channels) -## Update your emulator to the latest version -Before using the bot inspection middleware to debug your bot, you need to update your emulator to be version 4.5 or newer. Check the [latest version](https://github.com/Microsoft/BotFramework-Emulator/releases) for updates. +- Knowledge of bot [Middleware](v4sdk/bot-builder-concept-middleware.md) and [Managing state](v4sdk/bot-builder-concept-state.md) +- Knowledge of how to [Debug an SDK-first bot](bot-service-debug-bot.md) and [Test and debug with the Emulator](bot-service-debug-emulator.md) +- An install of the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md) +- An install [Dev Tunnel](https://aka.ms/devtunnels) (if you want to debug a bot configured in Azure to use other channels) +- A copy of the inspection bot sample for [C#](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/47.inspection), [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/47.inspection), or [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/47.inspection) -To check the version of your emulator, select **Help** -> **About** in the menu. You will see the current version of your emulator. +## Update your Emulator to the latest version -![current-version](./media/bot-debug-inspection-middleware/bot-debug-check-emulator-version.png) +Before using bot inspection middleware to debug your bot, update your Emulator to version 4.15 or later. Check the [latest version](https://github.com/Microsoft/BotFramework-Emulator/releases) for updates. -## Update your bot's code +To check the version of your Emulator, select **Help**, then **About** in the menu. You'll see the current version of your Emulator. -# [C#](#tab/csharp) -Set up the inspection state in the **Startup** file. Add the inspection middleware to the adapter. The inspection state is provided through dependency injection. See the code update below or refer to the inspection sample here: [C#](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/47.inspection). +## Update your bot code + +### [C#](#tab/csharp) + +The inspection state and inspection middleware are configured in the **Startup.cs** file and then used by the adapter. **Startup.cs** -[!code-csharp [inspection bot sample](~/../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/Startup.cs?range=17-37)] +[!code-csharp [inspection bot sample](../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/Startup.cs?range=24-25,35-37)] **AdapterWithInspection.cs** -[!code-csharp [inspection bot sample](~/../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/AdapterwithInspection.cs?range=11-37)] +[!code-csharp [inspection bot sample](../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/AdapterwithInspection.cs?range=13-36&highlight=20-21)] + +Update the bot class in the **EchoBot.cs** file. **EchoBot.cs** -[!code-csharp [inspection bot sample](~/../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/Bots/EchoBot.cs?range=14-43)] +[!code-csharp [inspection bot sample](../botbuilder-samples/samples/csharp_dotnetcore/47.inspection/Bots/EchoBot.cs?range=31-43&highlight=3-7)] -# [JavaScript](#tab/javascript) -Before updating your bot's code you should update its packages to the latest versions by executing the following command in your terminal: -```cmd -npm install --save botbuilder@latest -``` -Then you need to update the code of your JavaScript bot as follows. You can read more here: [Update your bot's code](https://github.com/Microsoft/BotFramework-Emulator/blob/master/content/CHANNELS.md#1-update-your-bots-code) or refer to the inspection sample here: [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection). +### [JavaScript](#tab/javascript) + +Before updating your bot's code, update its packages to the latest versions by executing the following command in your terminal: + +```console +npm install --save botbuilder@latest +``` + +Then you need to update the code of your JavaScript bot as follows. You can read more here: [Update your bot's code](https://github.com/Microsoft/BotFramework-Emulator/blob/master/content/CHANNELS.md#1-update-your-bots-code) or refer to the [Inspection middleware](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection) sample on GitHub. **index.js** -Set up the inspection state and add the inspection middleware to the adapter in the **index.js** file. +Set up the inspection state and add the inspection middleware to the adapter in the **index.js** file. -[!code-javascript [inspection bot sample](~/../botbuilder-samples/samples/javascript_nodejs/47.inspection/index.js?range=10-43)] +[!code-javascript [inspection bot sample](../botbuilder-samples/samples/javascript_nodejs/47.inspection/index.js?range=13-26,40-61)] **bot.js** -Update the bot class in the **bot.js** file. +Update the bot class in the **bot.js** file. + +[!code-javascript [inspection bot sample](../botbuilder-samples/samples/javascript_nodejs/47.inspection/bot.js?range=14-28)] -[!code-javascript [inspection bot sample](~/../botbuilder-samples/samples/javascript_nodejs/47.inspection/bot.js?range=6-50)] +### [Java](#tab/java) -# [Python](#tab/python) -Before updating your bot's code run install the necessary PyPI packages by running the following commands in a terminal: -```cmd +Set up the inspection state and add the inspection middleware to the adapter in the **Application.java** file. The inspection state is set by providing a new Spring @Bean to supply the BotFrameworkHttpAdapter that is set to be @Primary so it will override the default BotFrameworkHttpAdapter provided by the BotDependencyConfiguration base class. See the code update below or refer to the [Inspection middleware](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/47.inspection) sample on GitHub. + +**Application.java** +[!code-java [inspection bot sample](../botbuilder-samples/samples/java_springboot/47.inspection/src/main/java/com/microsoft/bot/sample/inspection/Application.java?range=66-97)] + +AdapterWithInspection is implemented as part of the com.microsoft.bot.integration package and can be reviewed from the Java SDK source code. + +Update the bot class in the **EchoBot.java** file. + +**EchoBot.java** +[!code-java [inspection bot sample](../botbuilder-samples/samples/java_springboot/47.inspection/src/main/java/com/microsoft/bot/sample/inspection/EchoBot.java?range=53-81)] + +### [Python](#tab/python) + +Before updating your bot's code, install the necessary PyPI packages by running the following commands in a terminal: + +```console pip install aiohttp pip install botbuilder-core>=4.7.0 ``` + Set up the inspection state in the **app.py** file by adding a middleware to the adapter. **app.py** -[!code-python [inspection bot sample](~/../botbuilder-samples/samples/python/47.inspection/app.py?range=75-84)] +[!code-python [inspection bot sample](../botbuilder-samples/samples/python/47.inspection/app.py?range=12-23,29-33,70-85)] Update the bot class in the **echo_bot.py** file. -**bots/echo_bot.py** +**bots/echo_bot.py** -[!code-python [inspection bot sample](~/../botbuilder-samples/samples/python/47.inspection/bots/echo_bot.py?range=16-64)] +[!code-python [inspection bot sample](../botbuilder-samples/samples/python/47.inspection/bots/echo_bot.py?range=48-64)] --- -## Test your bot locally -After updating the code you can run your bot locally and test the debugging feature using two emulators: one to send and receive messages, and the other to inspect the state of messages in debugging mode. To test your bot locally take the following steps: +## Test your bot locally -1. Navigate to your bot's directory in a terminal and execute the following command to run your bot locally: +After updating the code, you can run your bot locally and test the debugging feature using two Emulators: one to send and receive messages, and the other to inspect the state of messages in debugging mode. To test your bot locally: -# [C#](#tab/csharp) +1. Go to your bot's directory in a terminal and execute the following command to run your bot locally: -```cmd -dotnet run -``` + ### [C#](#tab/csharp) -# [JavaScript](#tab/javascript) + ```console + dotnet run + ``` -```cmd -npm start -``` + ### [JavaScript](#tab/javascript) -# [Python](#tab/python) -```cmd -python app.py -``` + ```console + npm start + ``` ---- + ### [Java](#tab/java) -2. Open your emulator. Click **Open Bot**. Fill in Bot URL with http://localhost:3978/api/messages and the **MicrosoftAppId** and **MicrosoftAppPassword** values. If you have a JavaScript bot you can find these values in your bot's **.env** file. If you have a C# bot you can find these values in the **appsettings.json** file. Click **Connect**. + ```console + mvn package + java -jar .\target\bot-inspection-sample.jar + ``` -3. Now open another emulator. This second emulator will work as a debugger. Follow the instructions as described in the previous step. Check **Open in debug mode** and then click **Connect**. + ### [Python](#tab/python) -4. At this point you will see a UUID (`/INSPECT attach `) in your debugging emulator. Copy the UUID and paste it to the chat box of the first emulator. + ```console + python app.py + ``` -> [!NOTE] -> A universally unique identifier (UUID) is a unique ID for identifying information. A UUID is generated every time when the emulator is launched in debug mode after you add the inspection middleware in your bot's code. + --- -5. Now you can send messages in the chat box of your first emulator and inspect the messages in the debugging emulator. To inspect the state of the messages click **Bot State** in the debugging emulator and unfold **values** on the right **JSON** window. You will be able to see the state of your bot as follows: -![bot state](./media/bot-debug-inspection-middleware/bot-debug-bot-state.png) +1. Open your Emulator. Select **Open Bot**. Fill in the **Bot URL** with `http://localhost:3978/api/messages` and the **MicrosoftAppId** and **MicrosoftAppPassword** values. If you have a JavaScript bot, you can find these values in your bot's **.env** file. If you have a C# bot, you can find these values in the **appsettings.json** file. For a Java bot you can find these values in the **application.properties** file. Select **Connect**. -## Inspect the state of a bot configured in Azure -If you want to inspect the state of your bot configured in Azure and connected to channels (like Teams) you will need to install and run [ngrok](https://ngrok.com/). +1. Now open another Emulator window. This second Emulator window will work as a debugger. Follow the instructions as described in the previous step. Check **Open in debug mode** and then select **Connect**. -### Run ngrok -At this point you have updated your emulator to the latest version and added the inspection middleware in your bot's code. The next step is to run ngrok and configure your local bot to your Azure Bot Channels Registration. Before running ngrok you need to run your bot locally. +1. At this point you'll see a command with a unique identifier (`/INSPECT attach `) in your debugging Emulator. Copy the whole command with the identifier from the debugging Emulator and paste it into the chat box of the first Emulator. -To run your bot locally do the following: -1. Navigate to your bot's folder in a terminal and set your npm registration to use the [latest builds](https://botbuilder.myget.org/feed/botbuilder-v4-js-daily/package/npm/botbuilder-azure) + > [!NOTE] + > A unique identifier is generated every time when the Emulator is launched in debug mode after you add the inspection middleware in your bot's code. -2. Run your bot locally. You will see your bot expose a port number like 3978. +1. Now you can send messages in the chat box of your first Emulator and inspect the messages in the debugging Emulator. To inspect the state of the messages, select **Bot State** in the debugging Emulator and unfold **values** on the right **JSON** window. You'll see the state of your bot in the debugging Emulator: -3. Open another command prompt and navigate to your bot's project folder. Run the following command: -``` -ngrok http 3978 -``` -4. ngrok is now connected to your locally running bot. Copy the public IP address. -![ngrok-success](./media/bot-debug-inspection-middleware/bot-debug-ngrok.png) + :::image type="content" source="./media/bot-debug-inspection-middleware/bot-debug-bot-state.png" alt-text="bot state"::: + +## Inspect the state of a bot configured in Azure + +If you want to inspect the state of your bot configured in Azure and connected to channels (like Teams) you'll need to install and run [Dev Tunnels](https://aka.ms/devtunnels). + +### Run devtunnel + +At this point, you've updated your Emulator to the latest version and added the inspection middleware in your bot's code. The next step is to run devtunnel and configure your local bot. Before running devtunnel you need to run your bot locally. + +To run your bot locally: + +1. Go to your bot's folder in a terminal and set your npm registration to use the [latest builds](https://botbuilder.myget.org/feed/botbuilder-v4-js-daily/package/npm/botbuilder-azure) + +1. Run your bot locally. You'll see your bot expose a port number like `3978`. + +1. Open another command prompt and go to your bot's project folder. Run the following command: + + ```console + devtunnel host -a -p 3978 + ``` + +1. devtunnel is now connected to your locally running bot. Copy the secure (HTTPS) public URL. + + :::image type="content" source="./media/debug-devtunnel/devtunnel-forwarding-url.png" alt-text="devtunnel success"::: + +### Update your bot resource + +Now that your local bot is connected to devtunnel, you can configure your bot resource in Azure to use the devtunnel URL. + +1. Go to your bot resource in Azure. On the left menu, under **Settings**, select **Configuration**. + 1. Set the **Messaging endpoint** to the devtunnel URL address you copied. If necessary add **/api/messages** after the IP address. For example, `https://0qg12llz-3978.usw2.devtunnels.ms/api/messages`. + 1. Select **Enable Streaming Endpoint**. + + :::image type="content" source="./media/bot-debug-inspection-middleware/bot-debug-channels-setting-devtunnel.png" alt-text="Set endpoint"::: + + 1. Select **Apply** to save your changes. + > [!TIP] + > If **Apply** isn't enabled, you can uncheck **Enable Streaming Endpoint** and select **Apply**, then check **Enable Streaming Endpoint** and select **Apply** again. You need to make sure that **Enable Streaming Endpoint** is checked and the configuration of the endpoint is saved. + +1. Go to your bot's resource group. + 1. Select **Deployment**, and then select the bot resource that previously deployed successfully. Select **Template** from the left menu to get the **MicrosoftAppId** and **MicrosoftAppPassword** for the web app associated with your bot. + + :::image type="content" source="./media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png" alt-text="Get inputs"::: -### Update channel registrations for your bot -Now that your local bot is connected to ngrok you can configure your local bot to your Bot Channels Registration in Azure. + 1. Update your bot's configuration file (**appsettings.json** for C#, or **.env** for JavaScript) with the **MicrosoftAppId** and **MicrosoftAppPassword**. -1. Go to your Bot Channels Registration in Azure. Click **Settings** on the left menu and set the **Messaging endpoint** with your ngrok IP. If necessary add **/api/messages** after the IP address. (For example, https://e58549b6.ngrok.io/api/messages). Check **Enable Streaming Endpoint** and **Save**. -![endpoint](./media/bot-debug-inspection-middleware/bot-debug-channels-setting-ngrok.png) -> [!TIP] -> If **Save** is not enabled, you can uncheck **Enable Streaming Endpoint** and click **Save**, then check **Enable Streaming Endpoint** and click **Save** again. You need to make sure that **Enable Streaming Endpoint** is checked and the configuration of the endpoint is saved. +1. Start your Emulator, select **Open Bot**, and enter `http://localhost:3978/api/messages` in the **Bot URL**. Fill **Microsoft App ID** and **Microsoft App password** with the same **MicrosoftAppId** and **MicrosoftAppPassword** you added to our bot's configuration file. Then select **Connect**. -2. Go to your bot's resource group, click **Deployment**, and select your Bot Channels Registration that previously deployed successfully. Click **Inputs** on the left side to get the **appId** and **appSecret**. Update your bot's **.env** file (or **appsettings.json** file if you have a C# bot) with the **appId** and **appSecret**. -![get-inputs](./media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png) +1. Your running bot is now connected to your bot resource in Azure. To test your bot in Azure in Web Chat, go to your bot resources, select **Test in Web Chat**, and send messages to your bot. -3. Start your emulator, click **Open Bot**, and put http://localhost:3978/api/messages in the **Bot URL**. Fill **Microsoft App ID** and **Microsoft App password** with the same **appId** and **appSecret** you added to our bot's **.env** (**appsettings.json**) file. Then click **Connect**. +### Enable debugging mode -4. Your running bot is now connected to your Bot Channels Registration in Azure. You can test the web chat by clicking **Test in Web Chat** and sending messages in the chat box. -![test-web-chat](./media/bot-debug-inspection-middleware/bot-debug-test-webchat.png) +1. In your Emulator, select **Debug**, then **Start Debugging**. +1. Enter the devtunnel URL (don't forget to add **/api/messages**) for the **Bot URL** (for example, `https://4jj51x75-51865.usw2.devtunnels.ms/api/messages`). + 1. For **Microsoft App ID**, enter your bot's app ID. + 1. For **Microsoft App password**, enter your bot's app secret. + 1. Make sure **Open in debug mode** is checked as well. + 1. Select **Connect**. -5. Now let's enable the debugging mode in the emulator. In your emulator select **Debug** -> **Start Debugging**. Fill the ngrok IP address (don't forget to add **/api/messages**) in the **Bot URL** (for example, https://e58549b6.ngrok.io/api/messages). Fill **Microsoft App ID** with **appId** and **Microsoft App password** with **appSecret**. Make sure **Open in debug mode** is checked as well. Click **Connect**. +1. With the debugging mode enabled, the Emulator generates a UUID. A UUID is a unique ID generated every time you start the debugging mode in your Emulator. +1. Copy and paste the UUID to the **Test in Web Chat** chat box for your channel's chat box. You'll see the message "Attached to session, all traffic is being replicated for inspection" in the chat box. -6. When the debugging mode is enabled a UUID will be generated in your emulator. A UUID is a unique ID generated every time you start the debugging mode in your emulator. Copy and paste the UUID to the **Test in Web Chat** chat box or your channel's chat box. You will see the message "Attached to session, all traffic is being replicated for inspection" in the chat box. +You can start debugging your bot by sending messages in the configured channel's chat box. Your local Emulator will automatically update the messages with all the details for debugging. To inspect your bot's state of messages, select **Bot State** and unfold the **values** in the right JSON window. - You can start debugging your bot by sending messages in the configured channel's chat box. Your local emulator will automatically update the messages with all the details for debugging. To inspect your bot's state of messages click **Bot State** and unfold the **values** in the right JSON window. +:::image type="content" source="./media/bot-debug-inspection-middleware/debug-state-inspection-channel-chat.gif" alt-text="debug-inspection-middleware"::: - ![debug-inspection-middleware](./media/bot-debug-inspection-middleware/debug-state-inspection-channel-chat.gif) +## Next steps -## Additional resources -- Try the new inspection bot samples here: [C#](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/47.inspection) and [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/47.inspection). -- Read [troubleshoot general problems](bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. -- Read the how to [Debug with the Emulator](bot-service-debug-emulator.md) article. +- Learn how to [Debug your bot using transcript files](v4sdk/bot-builder-debug-transcript.md). +- Learn how to [Debug a skill or skill consumer](v4sdk/skills-debug-skill-or-consumer.md). diff --git a/articles/bot-service-design-conversation-flow.md b/articles/bot-service-design-conversation-flow.md index 7a5bd0696..ee75c278d 100644 --- a/articles/bot-service-design-conversation-flow.md +++ b/articles/bot-service-design-conversation-flow.md @@ -1,128 +1,62 @@ --- -title: Design and control conversation flow - Bot Service -description: Learn how to design and control conversation flow in your bot to provide a good user experience. +title: Design and control conversation flow +description: Learn how to provide a good user experience with bots. Understand procedural conversation flow, interruption handling, and other design concepts. keywords: design, control, conversation flow, handle interruptions, overview -author: v-ducvo -ms.author: kamrani -manager: kamrani +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow ms.topic: article -ms.service: bot-service -ms.date: 11/19/2019 +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- - # Design and control conversation flow -::: moniker range="azure-bot-service-3.0" - -[!INCLUDE [pre-release-label](./includes/pre-release-label-v3.md)] - -In a traditional application, the user interface (UI) is a series of screens. -A single app or website can use one or more screens as needed to exchange information with the user. -Most applications start with a main screen where users initially land and provide navigation that leads to other screens for various functions like starting a new order, browsing products, or looking for help. - -Like apps and websites, bots have a UI, but it is made up of **dialogs**, rather than screens. Dialogs help preserve your place within a conversation, prompt users when needed, and execute input validation. They are useful for managing multi-turn conversations and simple "forms-based" collections of information to accomplish activities such as booking a flight. - -Dialogs enable the bot developer to logically separate various areas of bot functionality and guide conversational flow. For example, you may design one dialog to contain the logic that helps the user browse for products and a separate dialog to contain the logic that helps the user create a new order. - -Dialogs may or may not have graphical interfaces. They may contain buttons, text, and other elements, or be entirely speech-based. Dialogs also contain actions to perform tasks such as invoking other dialogs or processing user input. - -## Using dialogs to manage conversation flow - -[!INCLUDE [Dialog flow example](./includes/snippet-dotnet-manage-conversation-flow-intro.md)] - -For a detailed walkthrough of managing conversation flow using dialogs and the Bot Framework SDK, see: - -- [Manage conversation flow with dialogs (.NET)](./dotnet/bot-builder-dotnet-manage-conversation-flow.md) -- [Manage conversation flow with dialogs (Node.js)](./nodejs/bot-builder-nodejs-manage-conversation-flow.md) - -## Dialog stack - -When one dialog invokes another, the Bot Builder adds the new dialog to the top of the dialog stack. -The dialog that is on top of the stack is in control of the conversation. -Every new message sent by the user will be subject to processing by that dialog until it either closes or redirects to another dialog. -When a dialog closes, it's removed from the stack, and the previous dialog in the stack assumes control of the conversation. - -> [!IMPORTANT] -> Understanding the concept of how the dialog stack is constructed and deconstructed -> by the Bot Builder as dialogs invoke one another, close, and so on -> is critical to being able to effectively design the conversation flow of a bot. - -## Dialogs, stacks and humans - -It may be tempting to assume that users will navigate across dialogs, creating a dialog stack, -and at some point will navigate back in the direction they came from, unstacking the dialogs one by one in a neat and orderly way. -For example, the user will start at root dialog, invoke the new order dialog from there, and then invoke the product search dialog. -Then the user will select a product and confirm, exiting the product search dialog, complete the order, exiting the new order dialog, and arrive back at the root dialog. - -Although it would be great if users always traveled such a linear, logical path, it seldom occurs. -Humans do not communicate in "stacks." They tend to frequently change their minds. -Consider the following example: - -![bot](./media/bot-service-design-conversation-flow/stack-issue.png) - -While your bot may have logically constructed a stack of dialogs, - the user may decide to do something entirely different or ask a question that may be unrelated to the current topic. -In the example, the user asks a question rather than providing the yes/no response that the dialog expects. -How should your dialog respond? - -- Insist that the user answer the question first. -- Disregard everything that the user had done previously, reset the whole dialog stack, and start from the beginning by attempting to answer the user's question. -- Attempt to answer the user's question and then return to that yes/no question and try to resume from there. - -There is no one *right* answer to this question, as the best solution will depend upon the specifics of your scenario and how the user would reasonably expect the bot to respond. However, as your conversation complexity increases **dialogs** become harder to manage. For complex branchings situations, it may be easier to create your own flow of control logic to keep track of your user's conversation. - -## Next steps - -Managing the user's navigation across dialogs and designing conversation flow in a manner that enables -users to achieve their goals (even in a non-linear fashion) is a fundamental challenge of bot design. -The [next article](./bot-service-design-navigation.md) reviews some common pitfalls of poorly designed navigation and discusses strategies for avoiding those traps. - -::: moniker-end - -::: moniker range="azure-bot-service-4.0" +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] In a traditional application, the user interface (UI) consists of a series of screens, and a single app or website can use one or more screens as needed to exchange information with the user. Most applications start with a main screen where users initially land, and that screen provides navigation that leads to other screens for various functions like starting a new order, browsing products, or looking for help. -Like apps and websites, bots have a UI, but it is made up of **messages** rather than screens. Messages may contain buttons, text, and other elements, or be entirely speech-based. +Like apps and websites, bots have a UI, but it's made up of _messages_ rather than screens. Messages may contain buttons, text, and other elements, or be entirely speech-based. While a traditional application or website can request multiple pieces of information on a screen all at once, a bot will gather the same amount of information using multiple messages. In this way, the process of gathering information from the user is an active experience; one where the user is having an active conversation with the bot. -A well designed bot will have a conversation flow that feels natural. The bot should be able to handle the core conversation seamlessly, and be able to handle interruptions or switching topic of conversations gracefully. +A well-designed bot will have a conversational flow that feels natural. The bot should be able to handle the core conversation seamlessly and be able to handle interruptions or switching topics gracefully. ## Procedural conversation flow -Conversation with a bot is generally focused around the task a bot is trying to achieve, which is called a procedural flow. This is where the bot asks the user a series of questions to gather all the information it needs before processing the task. +Conversations with a bot can focus on the task a bot is trying to achieve, which is called a procedural flow. The bot asks the user a series of questions to gather all the information it needs before processing the task. -In a procedural conversation flow, you define the order of the questions and the bot will ask the questions in the order you defined. You can organize the questions into logical *modules* to keep the code centralized while staying focused on guiding the conversational. For example, you may design one module to contain the logic that helps the user browse for products and a separate module to contain the logic that helps the user create a new order. +In a procedural conversation flow, you define the order of the questions and the bot will ask the questions in the order you defined. You can organize the questions into logical groups to keep the code cMicrosoft Entralized while staying focused on guiding the conversation. For example, you may design one module to contain the logic that helps the user browse for products and a separate module to contain the logic that helps the user create a new order. -You can structure these modules to flow in any way you like, ranging from free form to sequential. The Bot Framework SDK provides several libraries that allows you to construct any conversational flow your bot needs. For example, the `prompts` library allows you to ask users for input, the `waterfall` library allows you to define a sequence of question/answer pair, the `dialog control` library allows you to modularized your conversational flow logic, etc. All of these libraries are tied together through a `dialogs` object. Let's take a closer look at how modules are implemented as `dialogs` to design and manage conversation flows and see how that flow is similar to the traditional application flow. +You can structure these modules to flow in any way you like, ranging from free form to sequential. The Bot Framework SDK provides a dialogs library that allows you to construct any conversational flow your bot needs. The library includes [waterfall dialogs](/azure/bot-service/bot-builder-concept-waterfall-dialogs) for creating a sequence of steps and prompts for asking users questions. For more information, see [Dialogs library](/azure/bot-service/bot-builder-concept-dialog). -![bot](./media/designing-bots/core/dialogs-screens.png) +:::image type="content" source="./media/designing-bots/core/dialogs-screens.png" alt-text="Diagram comparing application GUI flow against bot conversation flow."::: -In a traditional application, everything begins with the **main screen**. -The **main screen** invokes the **new order screen**. -The **new order screen** remains in control until it either closes or invokes other screens, such as the **product search screen**. -If the **new order screen** closes, the user is returned to the **main screen**. +In a traditional application, everything begins with the _main_ screen. +The main screen invokes the _new order_ screen. +The new order screen remains in control until it either closes or invokes other screens, such as the _product search_ screen. +If the new order screen closes, the user is returned to the main screen. -In a bot, everything begins with the **root dialog**. -The **root dialog** invokes the **new order dialog**. -At that point, the **new order dialog** takes control of the conversation and remains in control until it either closes or invokes other dialogs, such as the **product search dialog**. -If the **new order dialog** closes, control of the conversation is returned back to the **root dialog**. +In a bot that uses dialogs, everything begins with the _root dialog_. +The root dialog invokes the _new order dialog_. +At that point, the new order dialog takes control of the conversation and remains in control until it either closes or invokes another dialog, such as the _product search dialog_. +If the new order dialog closes, control of the conversation returns back to the root dialog. -For examples of how implement conversation flow, see how to [create your own prompts to gather user input](./v4sdk/bot-builder-primitive-prompts.md) and use dialogs to [implement sequential conversation flow](./v4sdk/bot-builder-dialog-manage-conversation-flow.md). +For an example of how to implement a conversational flow using the dialog libraries, see [Implement sequential conversation flow](./v4sdk/bot-builder-dialog-manage-conversation-flow.md). ## Handle interruptions It may be tempting to assume that users will perform procedural tasks one by one in a neat and orderly way. -For example, in a procedural conversation flow using `dialogs`, the user will start at root dialog, invoke the new order dialog from there, and then invoke the product search dialog. Then the user will select a product and confirm, exiting the product search dialog, complete the order, exiting the new order dialog, and arrive back at the root dialog. +For example, in a procedural conversation flow using dialogs, the user will start at the root dialog and invoke the new order dialog. From the new order dialog, they invoke the product search dialog. Then, when selecting one of the results listed in product search dialog, they invoke the new order dialog. After completing the order, they arrive back at the root dialog. Although it would be great if users always traveled such a linear, logical path, it seldom occurs. -Humans do not communicate in sequential `dialogs`. They tend to frequently change their minds. +People don't always communicate in sequential order. They tend to frequently change their minds. Consider the following example: -![bot](./media/bot-service-design-conversation-flow/stack-issue.png) +:::image type="content" source="./media/bot-service-design-conversation-flow/stack-issue.png" alt-text="Example of a user asking a question in response to a question from the bot."::: While your bot may be procedural centric, the user may decide to do something entirely different or ask a question that may be unrelated to the current topic. In the example above, the user asks a question rather than providing the yes/no response that the bot expects. @@ -132,12 +66,18 @@ How should your bot respond? - Disregard everything that the user had done previously, reset the whole dialog stack, and start from the beginning by attempting to answer the user's question. - Attempt to answer the user's question and then return to that yes/no question and try to resume from there. -There is no *right* answer to this question, as the best solution will depend upon the specifics of your scenario and how the user would reasonably expect the bot to respond. For more information see [Handle user interruption](v4sdk/bot-builder-howto-handle-user-interrupt.md). +There's no _right_ answer to this question, as the best solution will depend upon the specifics of your scenario and how the user would reasonably expect the bot to respond. See how to [Handle user interruptions](v4sdk/bot-builder-howto-handle-user-interrupt.md) for a bot that is designed to handle some types of interruptions. -## Next steps +## Expire a conversation + +Sometimes it's useful to restart a conversation from the beginning. For instance, if a user doesn't respond after a certain period of time. Different methods for ending a conversation include: -Managing the user's navigation across dialogs and designing conversation flow in a manner that enables -users to achieve their goals (even in a non-linear fashion) is a fundamental challenge of bot design. -The [next article](~/bot-service-design-navigation.md) reviews some common pitfalls of poorly designed navigation and discusses strategies for avoiding those traps. +- Track the last time a message was received from a user, and clear state if the time is greater than a preconfigured length upon receiving the next message from the user. +- Use a storage layer feature, such as Cosmos DB's [time-to-live](/azure/cosmos-db/how-to-time-to-live) feature, to clear state after a preconfigured length of time. + +For more information, see how to [Expire a conversation](v4sdk/bot-builder-howto-expire-conversation.md). + +## Next steps -::: moniker-end +Managing the user's navigation across dialogs and designing a conversational flow in a manner that enables users to achieve their goals (even in a non-linear fashion) is a fundamental challenge of bot design. +The [Design bot navigation](bot-service-design-navigation.md) article reviews some common pitfalls of poorly designed navigation and discusses strategies for avoiding those traps. diff --git a/articles/bot-service-design-first-interaction.md b/articles/bot-service-design-first-interaction.md index a66e1fff8..87e480cf9 100644 --- a/articles/bot-service-design-first-interaction.md +++ b/articles/bot-service-design-first-interaction.md @@ -1,54 +1,56 @@ --- -title: Design a bot's first user interaction - Bot Service +title: Design a bot's first user interaction description: Learn what makes a great first user experience and how to design your bots for success. -keywords: first impression, beginning, language vs menu -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +keywords: first impression, beginning, language versus menu +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Design a bot's first user interaction -## First impressions matter +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -The very first interaction between the user and bot is critical to the user experience. When designing your bot, keep in mind that there is more to that first message than just saying "hi." When you build an app, you design the first screen to provide important [navigation](bot-service-design-navigation.md) cues. Users should intuitively understand things such as where the menu is located and how it works, where to go for help, what the privacy policy is, and so on. When you design a bot, the user's first interaction with the bot should provide that same type of information. +First impressions matter. +The first interaction between the user and bot is critical to the user experience. When designing your bot, keep in mind that there's more to that first message than just saying "hi." When you build an app, you design the first screen to provide important [navigation](bot-service-design-navigation.md) cues. Users should intuitively understand things such as where the menu is located and how it works, where to go for help, what the privacy policy is, and so on. When you design a bot, the user's first interaction with the bot should provide that same type of information. -## Language versus menus +## Language versus menus Consider the following two designs: ### Design 1 -![bot](~/media/bot-service-design-first-interaction/hello1.png) - +:::image type="content" source="./media/bot-service-design-first-interaction/hello1.png" alt-text="A welcome message, without suggestions, 'Hello user, how can I help you?'"::: ### Design 2 -![bot](~/media/bot-service-design-first-interaction/hello2.png) +:::image type="content" source="./media/bot-service-design-first-interaction/hello2.png" alt-text="A welcome message, with suggestions: orders, products, or help."::: -Starting the bot with an open-ended question such as "How can I help you?" is generally not recommended. If your bot has a hundred different things it can do, chances are users won’t be able to guess most of them. Your bot didn’t tell them what it can do, so how can they possibly know? +Don't start your bot with an open-ended question, such as "How can I help you?". If your bot has a hundred different things it can do, chances are users won't be able to guess most of them. Your bot didn't tell them what it can do, so how can they possibly know? -Menus provide a simple solution to that problem. First, by listing the available options, your bot is conveying its capabilities to the user. Second, menus spare the user from having to type too much, instead they can just click. Finally, the use of menus can significantly simplify your natural language models by narrowing the scope of input that the bot could receive from the user. +Menus provide a simple solution to that problem. First, by listing the available options, your bot is conveying its capabilities to the user. Second, menus spare the user from having to type too much, instead they can just click. Finally, the use of menus can significantly simplify your natural language models by narrowing the scope of input that the bot could receive from the user. > [!TIP] -> Menus are a valuable tool when designing bots for a great user experience; don’t dismiss them as not being "smart enough." -> You can design your bot to use menus while still supporting free form input. -> If a user responds to the initial menu by typing rather than selecting a menu option, your bot could attempt to parse the user's text input. +> Menus are a valuable tool when designing bots for a great user experience; don't dismiss them as not being "smart enough." +> You can design your bot to use menus while still supporting free form input. +> If a user responds to the initial menu by typing rather than selecting a menu option, your bot could attempt to parse the user's text input. Alternatively, you can ask more pointed questions to lead the user if the bot has a specific function. For example, if your bot is responsible for taking sandwich orders, your first interaction could be "Hi! I'm here to take your sandwich order. What kind of bread would you like? We have white, wheat, or rye." That way, the user knows how to respond and is given navigational cues through the conversation. ## Other considerations -In addition to providing an intuitive and easily navigated first interaction, -a well-designed bot provides the user with access to information about its privacy policy and terms of use. +In addition to providing an intuitive and easily navigated first interaction, +a well-designed bot provides the user with access to information about its privacy policy and terms of use. > [!TIP] -> If your bot collects personal data from the user, it's important to convey that and to describe what will be done with the data. +> If your bot collects personal data from the user, it's important to convey that and to describe what'll be done with the data. ## Next steps -Now that you're familiar with some basic principles for designing the first interaction between user and bot, -learn more about [designing the flow of conversation](~/bot-service-design-conversation-flow.md). +Now that you're familiar with some basic principles for designing the first interaction between user and bot, +learn more about [designing the flow of conversation](bot-service-design-conversation-flow.md). diff --git a/articles/bot-service-design-navigation.md b/articles/bot-service-design-navigation.md index b75245286..317b9bed2 100644 --- a/articles/bot-service-design-navigation.md +++ b/articles/bot-service-design-navigation.md @@ -1,117 +1,120 @@ --- -title: Design bot navigation - Bot Service +title: Design bot navigation description: Learn how to design a good navigation structure for your bot and how to avoid the most common navigation design errors. keywords: navigation, overview, stubborn bot, clueless bot, mysterious bot, captain obvious bot, bot that can't forget -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 - +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Design bot navigation -Users can navigate websites using breadcrumbs, apps using menus, and web browsers using buttons like **forward** and **back**. However, none of these well-established navigation techniques entirely address navigation requirements within a bot. As discussed [previously](~/bot-service-design-conversation-flow.md#handle-interruptions), users often interact with bots in a non-linear fashion, making it challenging to design bot navigation that consistently delivers a great user experience. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Users can navigate websites using breadcrumbs, apps using menus, and web browsers using buttons like **forward** and **back**. However, none of these well-established navigation techniques entirely address navigation requirements within a bot. As discussed in [Design and control conversation flow](bot-service-design-conversation-flow.md#handle-interruptions), users often interact with bots in a non-linear fashion, making it challenging to design bot navigation that consistently delivers a great user experience. Consider the following dilemmas: -- How do you ensure that a user doesn't get lost in a conversation with a bot? -- Can a user navigate "back" in a conversation with a bot? -- How does a user navigate to the "main menu" during a conversation with a bot? -- How does a user "cancel" an operation during a conversation with a bot? +- How do you ensure that a user doesn't get lost in a conversation with a bot? +- Can a user navigate "back" in a conversation with a bot? +- How does a user navigate to the "main menu" during a conversation with a bot? +- How does a user "cancel" an operation during a conversation with a bot? -The specifics of your bot's navigation design will depend largely upon the features and functionality that your bot supports. Regardless of the type of bot you're developing, you'll want to avoid the common pitfalls of poorly designed conversational interfaces. This article describes these pitfalls in terms of five personalities: the "stubborn bot", the "clueless bot", the "mysterious bot", the "captain obvious bot", and the "bot that can't forget." +The specifics of your bot's navigation design depend largely upon the features and functionality that your bot supports. Regardless of the type of bot you're developing, you'll want to avoid the common pitfalls of poorly designed conversational interfaces. This article describes these pitfalls in terms of five personalities: the "stubborn bot", the "clueless bot", the "mysterious bot", the "captain obvious bot", and the "bot that can't forget." > [!TIP] > Mitigating each type of these personalities for your bot can often be done by correctly [handling user interruptions](v4sdk/bot-builder-howto-handle-user-interrupt.md). ## The "stubborn bot" -The stubborn bot insists upon maintaining the current course of conversation, -even when the user attempts to steer things in a different direction. +The stubborn bot insists upon maintaining the current course of conversation, +even when the user attempts to steer things in a different direction. -Consider the following scenario: +Consider the following scenario: -![bot](~/media/bot-service-design-navigation/stubborn-bot-new.png) +:::image type="content" source="media/bot-service-design-navigation/stubborn-bot-new.png" alt-text="Example of a stubborn bot asking the same question over and over again."::: -Users often change their minds, decide to cancel or sometimes they want to start over altogether. +Users often change their minds, decide to cancel or sometimes they want to start over altogether. > [!TIP] -> Do: Design your bot to consider that a user might attempt to change the course of the conversation at any time. +> _Do_: Design your bot to consider that a user might attempt to change the course of the conversation at any time. > -> Don't: Design your bot to ignore user input and keep repeating the same question in an endless loop. +> _Don't_: Design your bot to ignore user input and keep repeating the same question in an endless loop. -There are many methods of avoiding this pitfall, but perhaps the easiest way to prevent a bot from asking the same question endlessly is to simply specify a maximum number of retry attempts for each question. If designed in this manner, the bot is not doing anything "smart" to understand the user's input and respond appropriately but will at least avoid asking the same question in an endless loop. +One way to prevent a bot from asking the same question endlessly is to specify a maximum number of retry attempts for each question. If designed in this manner, the bot doesn't do anything _smart_ to understand the user input, but it does avoid asking the same question in an endless loop. ## The "clueless bot" The clueless bot responds in a nonsensical manner when it doesn't understand a user's attempt to access certain functionality. A user may try common keyword commands like "help" or "cancel" with reasonable expectations that the bot will respond appropriately. -Consider the following scenario: +Consider the following scenario: -![bot](~/media/bot-service-design-navigation/clueless-bot.png) +:::image type="content" source="media/bot-service-design-navigation/clueless-bot.png" alt-text="Example of a clueless bot accepting 'help' as a product code."::: -Although you may be tempted to design every dialog within your bot to listen for, and respond appropriately to, certain keywords, this approach is not recommended. +Although you may be tempted to design every dialog within your bot to listen for, and respond appropriately to, certain keywords, this approach isn't recommended. > [!TIP] -> Do: Implement [middleware](v4sdk/bot-builder-create-middleware.md) that will examine user input for the keywords that you specify (ex: "help", "cancel", "start over", etc.) and respond appropriately. -> -> Don't: Design every dialog to examine user input for a list of keywords. +> _Do_: Implement [middleware](v4sdk/bot-builder-create-middleware.md) that will examine user input for the keywords that you specify (ex: "help", "cancel", "start over", and so on) and respond appropriately. +> +> _Don't_: Design every dialog to examine user input for a list of keywords. -By defining the logic in your **middleware**, you're making it accessible to every exchange with the user. Using this approach, individual dialogs and prompts can be made to safely ignore the keywords, if necessary. +By defining the logic in your **middleware**, you make it accessible to every exchange with the user. Then, individual dialogs and prompts can be made to safely ignore the keywords, if necessary. ## The "mysterious bot" -The mysterious bot fails to immediately acknowledge the user's input in any way. +The mysterious bot fails to immediately acknowledge the user's input in any way. -Consider the following scenario: +Consider the following scenario: -![bot](~/media/bot-service-design-navigation/mysterious-bot.png) +:::image type="content" source="media/bot-service-design-navigation/mysterious-bot.png" alt-text="Example of a mysterious bot that doesn't respond to any of a user's messages."::: -In some cases, this situation might be an indication that the bot is having an outage. -However, it could just be that the bot is busy processing the user's input and hasn't yet finished compiling its response. +In some cases, this situation might be an indication that the bot's having an outage. +However, it could just be that the bot's busy processing the user's input and hasn't yet finished compiling its response. > [!TIP] -> Do: Design your bot to immediately acknowledge user input, even in cases where the bot may take some time to compile its response. -> -> Don't: Design your bot to postpone acknowledgement of user input until the bot finishes compiling its response. +> _Do_: Design your bot to immediately acknowledge user input, even in cases where the bot may take some time to compile its response. +> +> _Don't_: Design your bot to postpone acknowledgement of user input until the bot finishes compiling its response. -By immediately acknowledging the user's input, you eliminate any potential for confusion as to the state of the bot. If your response takes a long time to compile, consider sending a "typing" message to indicate your bot is working, and then following up with a [proactive message](v4sdk/bot-builder-howto-proactive-message.md) +By immediately acknowledging the user's input, you eliminate any potential for confusion as to the state of the bot. If your response takes a long time to compile, consider sending a "typing" message to indicate your bot's working, and then following up with a [proactive message](v4sdk/bot-builder-howto-proactive-message.md). ## The "captain obvious bot" -The captain obvious bot provides unsolicited information that is completely obvious and therefore useless to the user. +The captain obvious bot provides unsolicited information that's completely obvious and therefore useless to the user. Consider the following scenario: -![bot](~/media/bot-service-design-navigation/captainobvious-bot.png) +:::image type="content" source="media/bot-service-design-navigation/captainobvious-bot.png" alt-text="Example of a bot stating many things that are obvious to the user."::: > [!TIP] -> Do: Design your bot to provide information that will be useful to the user. -> -> Don't: Design your bot to provide unsolicited information that is unlikely to be useful to the user. +> _Do_: Design your bot to provide information that will be useful to the user. +> +> _Don't_: Design your bot to provide unsolicited information that's unlikely to be useful to the user. By designing your bot to provide useful information, you're increasing the odds that the user will engage with your bot. ## The "bot that can't forget" -The bot that can't forget inappropriately integrates information from past conversations into the current conversation. +The bot that can't forget inappropriately integrates information from past conversations into the current conversation. Consider the following scenario: -![bot](~/media/bot-service-design-navigation/rememberall-bot.png) +:::image type="content" source="media/bot-service-design-navigation/rememberall-bot.png" alt-text="Example of a bot that insists on completing an interaction from months ago."::: > [!TIP] -> Do: Design your bot to maintain the current topic of conversation, unless/until the user expresses a desire to revisit a prior topic. -> -> Don't: Design your bot to interject information from past conversations when it is not relevant to the current conversation. +> _Do_: Design your bot to maintain the current topic of conversation, unless/until the user expresses a desire to revisit a prior topic. +> +> _Don't_: Design your bot to interject information from past conversations when it's not relevant to the current conversation. -By maintaining the current topic of conversation, you reduce the potential for confusion and frustration as well as increasing the odds that the user will continue to engage with your bot. +By maintaining the current topic of conversation, you reduce the potential for confusion and frustration and increase the odds that the user will continue to engage with your bot. ## Next steps -By designing your bot to avoid these common pitfalls of poorly designed conversational interfaces, you're taking an important step toward ensuring a great user experience. +By designing your bot to avoid these common pitfalls of poorly designed conversational interfaces, you're taking an important step toward ensuring a great user experience. -Next, learn more about the [UX elements](~/bot-service-design-user-experience.md) that bots most typically rely upon to exchange information with users. +Next, learn more about the [UX elements](bot-service-design-user-experience.md) that bots most typically rely upon to exchange information with users. diff --git a/articles/bot-service-design-pattern-embed-app.md b/articles/bot-service-design-pattern-embed-app.md index 0f57f0851..c36b0dca4 100644 --- a/articles/bot-service-design-pattern-embed-app.md +++ b/articles/bot-service-design-pattern-embed-app.md @@ -1,88 +1,72 @@ --- -title: Embed a bot in an app - Bot Service -description: Learn how to design a bot that will be embedded within another app. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 08/15/2018 - +title: Embed a bot in an app +description: Learn how to embed bots in apps. See how to integrate bots with native mobile apps, web-based mobile apps, IoT apps, and other app types. View sample code. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: overview +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Embed a bot in an app -Although bots most commonly exist outside of apps, they can also be integrated with apps. For example, you could embed a [knowledge bot](~/bot-service-design-pattern-knowledge-base.md) within an app -to help users find information that might otherwise be challenging to locate within complex app structures. -You could embed a bot within a help desk app to act as the first responder to incoming user requests. -The bot could independently resolve simple issues and [hand off](~/bot-service-design-pattern-handoff-human.md) more complex issues to a human agent. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Although bots most commonly exist outside of apps, they can also be integrated with apps. For example, you could embed a [knowledge bot](bot-service-design-pattern-knowledge-base.md) within an app to help users find information that might otherwise be challenging to locate within complex app structures. + +You can embed a bot within a help desk app to act as the first responder to incoming user requests. +The bot can independently resolve simple issues and [hand off](bot-service-design-pattern-handoff-human.md) more complex issues to a human agent. ## Integrating bot with app -The way to integrate a bot with an app varies depending on the type of app. +The way to integrate a bot with an app varies depending on the type of app. ### Native mobile app -An app that is created in native code can communicate with the Bot Framework by using -the [Direct Line API][directLineAPI], -either via REST or websockets. +An app that's created in native code can communicate with the Bot Framework by using the [Direct Line API][directLineAPI], either via REST or web sockets. ### Web-based mobile app -A mobile app that is built by using web language and frameworks such as Cordova -may communicate with the Bot Framework by using the same components that a -[bot embedded within a website](~/bot-service-design-pattern-embed-web-site.md) would use, -just encapsulated within a native app's shell. +A mobile app built with a web language and frameworks such as [Cordova](https://cordova.apache.org/) may communicate with the Bot Framework by using the same components that a [bot embedded within a website](bot-service-design-pattern-embed-web-site.md) would use, just encapsulated within a native app's shell. ### IoT app -An IoT app can communicate with the Bot Framework by using -the [Direct Line API][directLineAPI]. -In some scenarios, it may also use Microsoft Cognitive Services -to enable capabilities such as image recognition and speech. +An IoT app can communicate with the Bot Framework by using the [Direct Line API][directLineAPI]. + +In some scenarios, it may also use [Azure AI services](/azure/ai-services/) like [Speech](/azure/ai-services/speech-service/), [Translator](/azure/ai-services/translator/), [Language](/azure/ai-services/language-service/), and [Vision](/azure/ai-services/computer-vision/). ### Other types of apps and games -Other types of apps and games can communicate with the Bot Framework by using -the [Direct Line API][directLineAPI]. +Other types of apps and games can communicate with the Bot Framework by using the [Direct Line API][directLineAPI]. ## Creating a cross-platform mobile app that runs a bot -This example of creating a mobile app that runs a bot uses Xamarin, a popular tool -for building cross-platform mobile applications. +This example of creating a mobile app that runs a bot uses [.NET MAUI](/dotnet/maui), a tool for building cross-platform applications. -First, create a simple web view component and use it to host a -web chat control. -Then, using Azure portal, add the Web Chat channel. +First, create a web view component and use it to host a [Web Chat](https://github.com/Microsoft/BotFramework-WebChat) control. Then, using Azure portal, add the Web Chat channel. -Next, specify the registered web chat URL as the source for the web view control in the Xamarin app: +Next, specify the registered web chat URL as the source for the web view control in the .NET MAUI app: -```cs +```csharp public class WebPage : ContentPage { - public WebPage() - { - var browser = new WebView(); - browser.Source = "https://webchat.botframework.com/embed/"; - this.Content = browser; - } +public WebPage() + { + var browser = new WebView(); + browser.Source = "https://webchat.botframework.com/embed/"; + this.Content = browser; + } } ``` -Using this process, you can create a cross-platform mobile application -that renders the embedded web view with the web chat control. - -![Back-channel](~/media/bot-service-design-pattern-embed-app/xamarin-apps.png) - - +Using this process, you can create a cross-platform application that renders the embedded web view with the web chat control. ## Additional resources - [Direct Line API][directLineAPI] -- Microsoft Cognitive Services +- [Azure AI services](/azure/ai-services/) [directLineAPI]: https://docs.botframework.com/restapi/directline3/#navtitle diff --git a/articles/bot-service-design-pattern-embed-web-site.md b/articles/bot-service-design-pattern-embed-web-site.md index e5a3207e6..17ffb4df9 100644 --- a/articles/bot-service-design-pattern-embed-web-site.md +++ b/articles/bot-service-design-pattern-embed-web-site.md @@ -1,67 +1,52 @@ --- -title: Embed a bot in a website - Bot Service -description: Learn how to design a bot that will be embedded within a website. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 - +title: Embed a bot in a website with Azure AI Bot Service +description: Learn how to embed bots in websites by using a web control. See how the backchannel mechanism facilitates private communication between web pages and bots. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Embed a bot in a website -Although bots commonly exist outside of websites, they can also be embedded within a website. -For example, you may embed a [knowledge bot](~/bot-service-design-pattern-knowledge-base.md) within a website -to enable users to quickly find information that might otherwise be challenging to locate within complex website structures. -Or you might embed a bot within a help desk website to act as the first responder to incoming user requests. -The bot could independently resolve simple issues and [handoff](~/bot-service-design-pattern-handoff-human.md) more complex issues to a human agent. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -This article explores integrating bots with websites and the process of using the *backchannel* mechanism to facilitate private communication between a web page and a bot. +Although bots commonly exist outside of websites, they can also be embedded within a website. For example, you may embed a [knowledge bot](bot-service-design-pattern-knowledge-base.md) within a website to enable users to quickly find information that might otherwise be challenging to locate within complex website structures. Or you might embed a bot within a help desk website to act as the first responder to incoming user requests. The bot could independently resolve simple issues and [handoff](bot-service-design-pattern-handoff-human.md) more complex issues to a human agent. -Microsoft provides two different ways to integrate a bot in a website: -the Skype web control and an open source web control. +This article explores how to integrate bots with websites and the process of using the *backchannel* mechanism to facilitate private communication between a web page and a bot. -## Skype web control - -The [Skype web control](https://aka.ms/bot-skype-web-control) is essentially a Skype client in a web-enabled control. Built-in Skype authentication enables the bot to authenticate and recognize users, without requiring the -developer to write any custom code. Skype will automatically recognize Microsoft Accounts used in its web client. - -Because the Skype web control simply acts as a front-end for Skype, -the user's Skype client automatically has access to the full context of any conversation that the web control facilitates. -Even after the web browser is closed, the user may continue to interact with the bot using the Skype client. +Microsoft provides two different ways to integrate a bot in a website: the [Skype web control](bot-service-channel-connect-skype.md) and an [open source web control](#open-source-web-control). ## Open source web control -The open source web chat control -is based upon ReactJS and uses the -[Direct Line API][directLineAPI] -to communicate with the Bot Framework. The web chat control provides a blank canvas for implementing the web chat, -giving you full control over its behaviors and the user experience that it delivers. +The open source [Web Chat](https://github.com/Microsoft/BotFramework-WebChat) control is based upon ReactJS and uses the [Direct Line API][directLineAPI] to communicate with the Bot Framework. The Web Chat control provides a blank canvas for implementing the Web Chat, giving you full control over its behaviors and the user experience that it delivers. -The *backchannel* mechanism enables the web page that is hosting the control -to communicate directly with the bot in a manner that is entirely invisible to the user. -This capability enables a number of useful scenarios: +The *backchannel* mechanism enables the web page that is hosting the control to communicate directly with the bot in a manner that is entirely invisible to the user. This capability enables a number of useful scenarios: -- The web page can send relevant data to the bot (e.g., GPS location). -- The web page can advise the bot about user actions (e.g., "user just selected Option A from the dropdown"). +- The web page can send relevant data to the bot, such as GPS location. +- The web page can advise the bot about user actions, such as "user just selected Option A from the dropdown". - The web page can send the bot the auth token for a logged-in user. -- The bot can send relevant data to the web page (e.g., current value of user's portfolio). -- The bot can send "commands" to the web page (e.g., change background color). +- The bot can send relevant data to the web page, such as the current value of user's portfolio. +- The bot can send "commands" to the web page, such as a change to the background color. ## Using the backchannel mechanism -[!INCLUDE [Introduction to backchannel mechanism](~/includes/snippet-backchannel.md)] +The [open source WebChat control](https://github.com/Microsoft/BotFramework-WebChat) communicates with bots by using the [Direct Line API](rest-api/bot-framework-rest-direct-line-3-0-concepts.md#client-libraries), which allows `activities` to be sent back and forth between client and bot. The most common type of activity is `message`, but there are other types as well. For example, the activity type `typing` indicates that a user is typing or that the bot is working to compile a response. + +You can use the backchannel mechanism to exchange information between client and bot without presenting it to the user by setting the activity type to `event`. The Web Chat control will automatically ignore any activities where `type="event"`. ## Sample code -The open source web chat control is available via GitHub. For details about how you can implement the backchannel mechanism using the open source web chat control and the Bot Framework SDK for Node.js, see [Use the backchannel mechanism](~/nodejs/bot-builder-nodejs-backchannel.md). +The open source [Web Chat](https://github.com/Microsoft/BotFramework-WebChat) control is available via GitHub. For details about how you can implement the backchannel mechanism using the open source Web Chat control and the Bot Framework SDK for Node.js, see [Use the backchannel mechanism](nodejs/bot-builder-nodejs-backchannel.md). ## Additional resources - [Direct Line API][directLineAPI] -- [Open source web chat control](https://github.com/Microsoft/BotFramework-WebChat) -- [Use the backchannel mechanism](~/nodejs/bot-builder-nodejs-backchannel.md) +- [Open source Web Chat control](https://github.com/Microsoft/BotFramework-WebChat) +- [Use the backchannel mechanism](nodejs/bot-builder-nodejs-backchannel.md) -[directLineAPI]: https://docs.botframework.com/restapi/directline3/#navtitle +[directLineAPI]: rest-api/bot-framework-rest-direct-line-3-0-concepts.md#client-libraries diff --git a/articles/bot-service-design-pattern-handoff-human.md b/articles/bot-service-design-pattern-handoff-human.md index 22aba32c3..27fd0a121 100644 --- a/articles/bot-service-design-pattern-handoff-human.md +++ b/articles/bot-service-design-pattern-handoff-human.md @@ -1,97 +1,137 @@ --- -title: Transition conversations from bot to human - Bot Service -description: Learn how to design for situations where a user starts a conversation with a bot and then must be handed off to a human. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 5/2/2019 - +title: Transition conversations from bot to human +description: Learn how to design for situations where a user starts a conversation with a bot and then must be handed off to a human. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Transition conversations from bot to human -Regardless of how much artificial intelligence a bot possesses, there may still be times when it needs to hand off the conversation to a human being. The bot should recognize when it needs to hand off and provide the user with a clear, smooth transition. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## Scenarios that require human involvement +Regardless of how much artificial intelligence a bot possesses, it might still need to hand off the conversation to a human being. Such a hand off might be necessary if the bot doesn't understand the user (because of an AI limitation), or if the request can't be automated and requires a human action. In such cases, the bot should recognize when it needs to hand off the conversation and provide the user with a smooth transition. -A wide variety of scenarios may require that a bot transition control of the conversation to a human. A few of those scenarios are *triage*, *escalation*, and *supervision*. +Microsoft Bot Framework is an open platform that allows developers to integrate with various agent engagement platforms. -### Triage +## Handoff integration models -A typical help desk call starts with some very basic questions that can easily be answered by a bot. As the first responder to inbound requests from users, a bot could collect the user's name, address, description of the problem, or any other pertinent information and then transition control of the conversation to an agent. Using a bot to triage incoming requests allows agents to devote their time to solving the problem instead of collecting information. +Microsoft Bot Framework supports two models for integration with agent engagement platforms. The handoff protocol is identical for both models, however the onboarding details differ between the models and the agent engagement platforms. -### Escalation +The goal isn't to offer a universal solution for integration with any customer's system, but rather to provide a _common language_ and _best practices_ for bot developers and system integrators with which to build conversational AI systems with a human in the loop. -In the help desk scenario, a bot may be able to answer basic questions and resolve simple issues in addition to collecting information, such as resetting a user's password. However, if a conversation indicates that a user's issue is complex enough to require human involvement, the bot will need to escalate the issue to a human agent. To implement this type of scenario, a bot must be capable of differentiating between issues it can resolve independently and issues that must be escalated to a human. There are many ways that a bot may determine that it needs to transfer control of the conversation to a human. +### Bot as an agent -#### User-driven menus +In the first model, known as _bot as an agent_, the bot joins the ranks of the live agents connected to the agent hub and responds to user requests as if the requests came from any other Bot Framework channel. The conversation between the user and the bot can be escalated to a human agent, at which point the bot disengages from the active conversation. -Perhaps the simplest way for a bot to handle this dilemma is to present the user with a menu of options. Tasks that the bot can handle independently appear in the menu above a link labeled "Chat with an agent." This type of implementation requires no advanced machine learning or natural language understanding. The bot simply transfers control of the conversation to a human agent when the user selects the "Chat with an agent" option. +The main advantage of this model is its simplicity—you can add an existing bot to the agent hub with minimal effort, and the agent hub will handle the complexity of message routing. -#### Scenario-driven +:::image type="content" source="media/designing-bots/patterns/bot-as-agent-2.PNG" alt-text="Diagram of an agent hub that can direct messages to a bot or human agents."::: -The bot may decide whether or not to transfer control based upon whether or not it determines that it is capable of handling the scenario at hand. The bot collects some information about the user's request and then queries its internal list of capabilities to determine if it is capable of addressing that request. If the bot determines that it is capable of addressing the request, it does so, but if the bot determines that the request is beyond the scope of issues it can resolve it transfers control of the conversation to a human agent. +### Bot as a proxy -#### Natural language +The second model is known as _bot as a proxy_. The user talks directly to the bot, until the bot decides that it needs help from a human agent. The message router component in the bot redirects the conversation to the agent hub, which dispatches it to the appropriate agent. The bot stays in the loop and can collect the transcript of the conversation, filter messages, or provide additional content to both the agent and the user. -Natural language understanding and sentiment analysis help the bot decide when to transfer control of the conversation to a human agent. This is particularly valuable when attempting to determine when the user is frustrated or wants to speak with a human agent. - -The bot analyzes the content of the user's messages -by using the Text Analytics API -to infer sentiment -or by using the LUIS API. +Flexibility and control are the main advantages of this model. The bot can support multiple channels and have control over how the conversations are escalated and routed between the user, the bot, and the agent hub. +:::image type="content" source="media/designing-bots/patterns/bot-as-proxy-2.PNG" alt-text="Diagram of a bot that can route messages to an agent hub."::: -> [!TIP] -> Natural language understanding may not always be the best method for determining when a bot -> should transfer conversation control to a human being. Bots, like humans, don't always guess -> correctly, and invalid responses will frustrate the user. If the user selects from a menu of -> valid choices, however, the bot will always respond appropriately to that input. +## Handoff protocol -### Supervision +The protocol is centered around events for initiation, sent by the bot to the channel, and status update, sent by the channel to the bot. -In some cases, the human agent will want to monitor the conversation instead of taking control. +### Handoff initiation -For example, consider a help desk scenario where a bot is communicating with a user to diagnose computer problems. A machine learning model helps the bot determine the most likely cause of the issue, however before advising the user to take a specific course of action, the bot can privately confirm the diagnosis and remedy with the human agent and request authorization to proceed. The agent then clicks a button, the bot presents the solution to the user, and the problem is solved. The bot is still performing the majority of the work, but the agent retains control the final decision. +A _handoff initiation_ event is created by the bot to initiate handoff. -## Transitioning control of the conversation +The event can include: -When a bot decides to transfer control of a conversation to a human, it can inform the user that she is being transferred and put the conversation into a 'waiting' state until it confirms that an agent is available. +- The context of the handoff request, to route the conversation to an appropriate agent. +- A transcript of the conversation, so an agent can read the conversation that took place between the customer and the bot before the handoff was initiated. -When the bot is waiting for a human, it may automatically answer all incoming user messages with a default response such as "waiting in queue". Furthermore, you could have the bot remove the conversation from the 'waiting' state if the user sent certain messages such as "never mind" or "cancel". +The following are common handoff initiation event properties: -You specify how agents will be assigned to waiting users when you design your bot. For example, the bot may implement a simple queue system: first in, first out. More complex logic would assign users to agents based upon geography, language, or some other factor. The bot could also present some type of UI to the agent that they can use to select a user. When an agent becomes available, she connects to the bot and joins the conversation. +- Name: Required, the _name_ property must be set to "handoff.initiate". +- Conversation: Required, the _conversation_ property describes the conversation in which the activity exists. Conversation _must_ include the conversation `Id`. +- Value: Optional, the _value_ property can contain agent hub-specific JSON content that the hub can use to route the conversation to a relevant agent. +- Attachments: Optional, the _attachments_ property can include a transcript as an attachment. The Bot Framework defines a _transcript_ attachment type. An attachment can be sent either inline (subject to a size limit) or offline by providing `ContentUrl`. -> [!IMPORTANT] -> Even after an agent is engaged, the bot remains the behind-the-scenes facilitator of the conversation. -> The user and agent never communicate directly with each other; they just route messages through the bot. + ```csharp + handoffEvent.Attachments = new List { + new Attachment { + Content = transcript, + ContentType = "application/json", + Name = "Transcript", + } + }; + ``` -## Routing messages between user and agent + > [!NOTE] + > Agent hubs **must ignore** attachment types they don't understand. -After the agent connects to the bot, the bot begins to route messages between user and agent. Although it may appear to the user and the agent that they are chatting directly with each other, they are actually exchanging messages via the bot. The bot receives messages from the user and sends those messages to the agent, and in turn receives messages from the agent and sends those messages to the user. +When a bot detects the need to hand the conversation off to an agent, it signals its intent by sending a handoff initiation event. +The SDK for C# includes a `CreateHandoffInitiation` method to create a valid handoff initiation event. + +```csharp +var activities = GetRecentActivities(); +var handoffContext = new { Skill = "credit cards" }; +var handoffEvent = + EventFactory.CreateHandoffInitiation( + turnContext, handoffContext, new Transcript(activities)); +await turnContext.SendActivityAsync(handoffEvent); +``` + +### Handoff status + +A _handoff status_ event is sent to the bot by the agent hub. The event informs the bot about the status of the initiated handoff operation. > [!NOTE] -> In more advanced scenarios, the bot can assume responsibility beyond merely routing messages -> between user and agent. For example, the bot may decide which response is appropriate -> and simply ask the agent for confirmation to proceed. +> Bots are _not required_ to handle a handoff status event; however, they _must not_ reject it. -## Additional resources +The following are common handoff status event fields: -::: moniker range="azure-bot-service-4.0" +- Name: Required, the _name_ property must be set to "handoff.status". +- Conversation: Required, the _conversation_ property describes the conversation in which the activity exists. Conversation _must_ include the conversation `Id`. +- Value: Required, the _value_ property that describes the current status of the handoff operation. The value has the following properties. + - State: Required, the _state_ property can have one of these values: -- [Dialogs](v4sdk/bot-builder-dialog-manage-conversation-flow.md) -- Text Analytics API + | Value | Meaning | + |:------------|:-----------------------------------------------------------------------------------------------------------------| + | "accepted" | An agent accepted the request and taken control of the conversation. | + | "failed" | The handoff request failed. The _message_ property might contain additional information relevant to the failure. | + | "completed" | The handoff request completed. | + + - Message: Optional, the _message_ property is an object defined by the agent hub. -::: moniker-end + Here are some example value objects: -::: moniker range="azure-bot-service-3.0" + ```json + { "state" : "completed" } + ``` -- [Manage conversation flow with dialogs (.NET)](~/dotnet/bot-builder-dotnet-manage-conversation-flow.md) -- [Manage conversation flow with dialogs (Node.js)](~/nodejs/bot-builder-nodejs-manage-conversation-flow.md) -- Text Analytics API + ```json + { "state" : "failed", "message" : "Can't find agent with requested skill" } + ``` +## Handoff library -::: moniker-end +The [Handoff Library](https://github.com/microsoft/BotBuilder-Samples/tree/main/experimental/handoff-library) has been created to complement the Bot Framework v4 SDK in supporting handoff; specifically: +- Implements the additions to the Bot Framework SDK to support handoff to an agent (also known as _escalation_). +- Contains definitions of three event types for signaling handoff operations. + +> [!NOTE] +> Integrations with specific agent hubs are not part of the library. + +## Additional resources + +- [Integration with Microsoft Dynamics Omnichannel for Customer Service](https://github.com/microsoft/BotBuilder-Samples/tree/master/experimental/handoff-library/csharp_dotnetcore/samples) +- [Integration with LivePerson LiveEngage platform](https://developers.liveperson.com/third-party-bots-microsoft-bot-framework.html) +- [Dialogs](v4sdk/bot-builder-dialog-manage-conversation-flow.md) +- [Azure AI services](/azure/ai-services/) diff --git a/articles/bot-service-design-pattern-integrate-browser.md b/articles/bot-service-design-pattern-integrate-browser.md deleted file mode 100644 index 5bab7abf5..000000000 --- a/articles/bot-service-design-pattern-integrate-browser.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: Integrate your bot with a web browser - Bot Service -description: Learn how to design a smooth user transition from bot to web browser and back again. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 ---- - -# Integrate your bot with a web browser - -Some scenarios require more than just a bot to fulfill a requirement. -A bot may need to send the user to a web browser to complete a task and then resume the conversation with the user after the task has been completed. - -## Authentication and authorization -If a bot wants the ability to read the user's calendar in Office 365, or perhaps -even create appointments on behalf of that user, the user must first authenticate with Microsoft Azure Active Directory and -authorize the bot to access the user's calendar data. The bot will redirect the user to a web browser to complete the -authentication and authorization tasks, and then will subsequently resume the conversation with the user. - -## Security and compliance -Security and compliance requirements often restrict the type of information that a bot -can exchange with a user. In some cases, it may be necessary for the user to send/receive data -outside of the current conversation. -For example, if a user wants to execute a payment using a third-party payment provider, a credit card number should not -be specified within the context of the conversation. -Instead, the bot will direct the user to a web browser to complete the payment process, -and then will subsequently resume the conversation with the user. - -This article explores the process of facilitating a user's transition from -bot to web browser, and back again. - -> [!NOTE] -> Transitioning from chat to web browser and back is not ideal, as switching between -> applications can easily confuse a user. To provide a better experience, many channels -> offer built-in HTML windows that a bot can use to present applications would otherwise -> appear in a web browser. This technique allows the user to remain within the conversation -> while still accessing external resources. This approach is conceptually similar to mobile -> applications managing authorization flows using OAuth within embedded web views. - -## Bot to web browser, and back again - -This diagram shows the high-level flow for integration between bot and web browser. - -![Bot to web interaction](~/media/bot-service-design-pattern-integrate-browser/bot-to-web1.png) - -Consider each step of the flow: - -1. The bot generates and displays a hyperlink that will redirect the user to a website. - The hyperlink typically includes data via querystring parameters on the target URL that specify information about the context of the current conversation, such as conversation ID, channel ID, and user ID in the channel. - -2. The user clicks the hyperlink and is redirected to the target URL within a web browser. - -3. The bot enters a state awaiting communication from the website to indicate that the website flow is complete. - > [!TIP] - > Design this flow so that the bot will not permanently remain in the 'waiting' state if - > the user never completes the website flow. In other words, if the user abandons the web - > browser and starts communicating with the bot again, the bot should acknowledge, not [ignore](~/bot-service-design-navigation.md#the-mysterious-bot) - > that input. - -4. The user completes the necessary task(s) via the web browser. - This could be an OAuth flow or any sequence of events required by the scenario at hand. - -5. When the user completes the website flow, the website generates a '[magic number](#verify-identity)' - and instructs the user to copy the value and paste it back into the conversation with the bot. - -6. The website [signals to the bot](#website-signal-to-bot) that the user has completed the website flow. - It communicates the 'magic number' to the bot and provides any other relevant data. - For example, in the case of an OAuth flow, the website would provide an access token to the bot. - -7. The user returns to the bot and pastes the 'magic number' into the chat. - The bot validates that 'magic number' provided by the user matches the expected value, verifying that the current user is the same user who previously clicked the hyperlink to initiate the website flow. - -### Verifying user identity using the 'magic number' - -The generation of a 'magic number' during the bot-to-website flow ([step 5](#generate-magic-number) above) enables the bot to subsequently verify that the user who initiated the website flow is indeed the user -for whom it was intended. -For example, if a bot is conducting a group chat with multiple users, any one of them -could have clicked the hyperlink to initiate the website flow. Without the 'magic number' validation process, -the bot has no way of knowing which user completed the flow. -One user could authenticate and inject access tokens in another user's session. - -> [!WARNING] -> This isn't just a risk within group chats. Without the 'magic number' validation process, anyone who obtains the hyperlink to launch the website flow can spoof a user's identity. - -The magic number should be a random number generated using a strong cryptography library. -For an example of the generation process in C#, see -this code -within the BotAuth library. -BotAuth enables bots that are built in Microsoft Bot Framework to implement -the bot-to-website flow to authenticate a user in a website and then to subsequently use the access token -that was generated from the authentication process. -Since BotAuth does not make any assumptions about the channel's capabilities, such flows should function well with most channels. - -> [!NOTE] -> The need for the 'magic number' validation process should be deprecated as channels build their own embedded web views. - -### How does the website 'signal' the bot? - -When the bot [generates the hyperlink](#generate-hyperlink) that the user will click to initiate the website flow, -it includes information via querystring parameters in the target URL about the context of the current conversation, such as conversation ID, channel ID, and user ID in the channel. The website can subsequently use this information to read and write state variables for that user or conversation using the Bot Framework SDK or REST APIs. See [step 6](#signal-to-bot) above for an example of how the website 'signals' to the bot that the website flow is complete. - -## Sample code - -As described in this article, the BotAuth library enables OAuth flows to be bound to bots that are built using .NET and Node in the Microsoft Bot Framework. - -## Additional resources - -- [Dialogs](~/dotnet/bot-builder-dotnet-dialogs.md) -- [Manage conversation flow with dialogs (.NET)](~/dotnet/bot-builder-dotnet-manage-conversation-flow.md) -- [Manage conversation flow with dialogs (Node.js)](~/nodejs/bot-builder-nodejs-manage-conversation-flow.md) diff --git a/articles/bot-service-design-pattern-knowledge-base.md b/articles/bot-service-design-pattern-knowledge-base.md index 8553095bd..20d13d1a3 100644 --- a/articles/bot-service-design-pattern-knowledge-base.md +++ b/articles/bot-service-design-pattern-knowledge-base.md @@ -1,192 +1,126 @@ --- -title: Design knowledge bots - Bot Service +title: Design knowledge bots description: Learn about different ways to design a knowledge bot that finds and returns information in response to the user's input or query. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 - +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Design knowledge bots -A knowledge bot can be designed to provide information about virtually any topic. -For example, one knowledge bot might answer questions about events such as, "What bot events are there at this conference?", "When is the next Reggae show?", or "Who is Tame Impala?" -Another might answer IT-related questions such as "How do I update my operating system?" or "Where do I go to reset my password?" -Yet another might answer questions about contacts such as "Who is John Doe?" or "What is Jane Doe's email address?" +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -Regardless of the use case for which a knowledge bot is designed, its basic objective is always the same: find and return the information that the user has requested -by leveraging a body of data, such as relational data in a SQL database, -JSON data in a non-relational store, or PDFs in a document store. +You can design a knowledge bot that covers virtually any topic. +Regardless of the use case for which a knowledge bot is designed, its basic objective is always the same: find and return the information that the user has requested by searching a body of data. -## Search +For example, one knowledge bot might answer questions about events such as, "What bot events are there at this conference?", "When is the next Reggae show?", or "Who is Tame Impala?" Another might answer IT-related questions such as "How do I update my operating system?" or "Where do I go to reset my password?". Yet another might answer questions about contacts such as "Who is John Doe?" or "What is Jane Doe's email address?". -Search functionality can be a valuable tool within a bot. +This article covers some of the AI capabilities you can add to a bot, such as to let a user search for information, ask questions, or interact with information. +For which Azure AI services features the Bot Framework SDK supports, see [Natural language understanding](v4sdk/bot-builder-concept-luis.md). -First, "fuzzy search" enables a bot to return information that's likely to be relevant to the user's question, without requiring that the user provide precise input. -For example, if the user asks a music knowledge bot for information about "impala" (instead of "Tame Impala"), the bot can respond with information that's most likely to be relevant to that input. - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/fuzzySearch2.png) - -Search scores indicate the level of confidence for the results of a specific search, -enabling a bot to order its results accordingly, or even tailor its communication based upon confidence level. -For example, if confidence level is high, the bot may respond with "Here is the event that best matches your search:". - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/searchScore2.png) - -If confidence level is low, the bot may respond with "Hmm... were you looking for any of these events?" - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/searchScore1.png) - -### Using Search to Guide a Conversation - -If your motivation for building a bot is to enable basic search engine functionality, -then you may not need a bot at all. What does a conversational interface offer that users can't get from a typical search engine in a web browser? - -Knowledge bots are generally most effective when they are designed to guide the conversation. -A conversation is composed of a back-and-forth exchange between user and bot, which presents the bot -with opportunities to ask clarifying questions, present options, and validate outcomes -in a way that a basic search is incapable of doing. -For example, the following bot guides a user through a conversation that facets and filters a dataset until it -locates the information that the user is seeking. - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/guidedConvo1.png) - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/guidedConvo2.png) - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/guidedConvo3.png) - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/guidedConvo4.png) - -By processing the user's input in each step and presenting the relevant options, the bot guides the user to the information that they're seeking. Once the bot delivers that information, it can even provide guidance about more efficient ways to find similar information in the future. - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/Training.png) - -### Azure Search - -By using Azure Search, -you can create an efficient search index that a bot can easily search, facet, and filter. -Consider a search index that is created using the Azure portal. - -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/search3.PNG) +> [!TIP] +> Azure AI services incorporates evolving technologies. This article describes both newer and older features. -You want to be able to access all properties of the data store, so you set each property as "retrievable." -You want to be able to find musicians by name, so you set the **Name** property as "searchable." -Finally, you want to be able to facet filter over musicians' eras, so you mark the **Eras** property as both "facetable" and "filterable." +## About confidence scores -Faceting determines the values that exist in the data store for a given property, along with the magnitude of each value. -For example, this screenshot shows that there are 5 distinct eras in the data store: +Some features enable a bot to return information from a knowledge base or language model to match a user question or query. -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/facet.png) +For example, if the user asks a music knowledge bot for information about "impala" (instead of band's full name "Tame Impala"), the bot can respond with information that's most likely to be relevant to that input. Similarly, language understanding features can use a language model to extract the likely _intent_ from user input. +For example, if the user asks a travel agent bot to "book a room for three days", the bot might extract a "reserve a room" intent and follow up by collecting details. -Filtering, in turn, selects only the specified instances of a certain property. -For example, you could filter the result set above to contain only items where **Era** is equal to "Romantic." +Both search and intent recognition return a confidence score, which indicates the level of confidence the engine has that a particular result is correct. Use confidence scores to order results or to respond differently, based on overall confidence in your answer. > [!NOTE] -> See a sample bot -> for a complete example of a knowledge bot that is created using Azure Document DB, Azure Search, and the -> Microsoft Bot Framework. -> -> For the sake of simplicity, the example above shows a search index that is created using the Azure portal. -> Indices can also be created programmatically. +> When you use a combination of different service or feature types together, test inputs with each of the tools to determine the threshold score for each of your models. The services and features use different scoring criteria, so the scores generated across these tools are not directly comparable. For example, the QnA Maker service used a confidence range of 0 to 100, while the question answering feature uses a range of 0.0 to 1.0. -## QnA Maker +- If confidence is high, your bot might respond with "Here's the event that best matches your search" or "I can help you reserve a room" and present the top answer or start asking follow-up questions. +- If confidence is low, your bot might respond with "Hmm... were you looking for any of these events?" or "I can help you with the following things:" and present a list of possible answers or options. -Some knowledge bots may simply aim to answer frequently asked questions (FAQs). -QnA Maker -is a powerful tool that's designed specifically for this use case. -QnA Maker has the built-in ability to scrape questions and answers from an existing FAQ site, plus it also allows you to manually configure your own custom list of questions and answers. -QnA Maker has natural language processing abilities, enabling it to even provide answers to questions that are worded slightly differently than expected. -However, it does not have semantic language understanding abilities. -It cannot determine that a puppy is a type of dog, for example. +## To filter topics -Using the QnA Maker web interface, you can configure a knowledge base with three question and answer pairs: +You can design knowledge bots to help a user narrow and refine a search. +Within a conversation, the bot can ask clarifying questions, present options, and validate outcomes, in a way that basic search can't. -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/KnowledgeBaseConfig.png) +For example, an events bot can find out what type of event the user is interested in by asking a series of questions. +Consider the following exchange: -Then, you can test it by asking a series of questions: +1. User, "events". +1. Bot, "What are you interested in? Music, Comedy, Film...". +1. User, "Music". +1. Bot, "What type of music are you interested in? Any, Rock/Pop, Hip-hop/Rap, ...". +1. User, "Rock/Pop". +1. Bot, "What day would you like to see Rock/Pop? Friday, Saturday, Sunday, Any". +1. User, "Saturday". +1. Bot, "Here are the Rock/Pop shows for Saturday:", with a list of the found shows. -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/exampleQnAConvo.png) +By processing the user's input in each step and presenting relevant options, the bot guides the user to the information that they're seeking. +Once the bot delivers that information, it can also provide guidance about more efficient ways to find similar information in the future. -The bot correctly answers the questions that directly map to the ones that were configured in the knowledge base. However, it incorrectly responds to the question "can I bring my tea?", because this question is most similar in structure to the question "can I bring my vodka?." The reason QnA Maker gives an incorrect answer is that it does not inherently understand the meaning of words. It does not know that "tea" is a type of nonalcoholic drink. Therefore, it answers "Alcohol is not allowed." +> By the way, you can also just type "Rock friday" or search for an event by name. -> [!TIP] -> Create your QnA pairs and then test and re-train your bot by using -> the "Inspect" button under the conversation to select an alternative answer for each -> incorrect answer that is given. +For information about related Azure services, see [Search](v4sdk/bot-builder-concept-luis.md#search) in the **Natural language understanding** concept article. -## LUIS +## To answer questions -Some knowledge bots require natural language processing (NLP) capabilities so that they can -analyze a user's messages to determine the user's intent. -[Language Understanding (LUIS)](https://www.luis.ai) provides a fast and effective means of adding NLP capabilities to bots. -LUIS enables you to use existing, pre-built models from Bing and Cortana whenever they meet your needs, as well as allowing you to create specialized models of your own. +You can design knowledge bots to answer frequently asked questions. +Services that support question and answer features often allow you or your bot to: -When working with huge datasets, it's not necessarily feasible to train an NLP model with every variation of an entity. -In a music playing bot, for example, a user might message "Play Reggae", "Play Bob Marley", or "Play One Love". -Although a bot could map each of these messages to the intent "playMusic", -without being trained with every artist, genre and song name, -an NLP model would not be able to identify whether the entity is a genre, artist or song. -By using an NLP model to identify the generic entity of type "music", the bot could search -its data store for that entity, and proceed from there. +- Manage and train a knowledge base. +- Import information into a knowledge base, such as from a data file or web page. +- Guess which answer best maps to the user's question. +- Ask the user follow-up questions to help find the answer they're looking for. -## Combining Search, QnA Maker, and/or LUIS +For information about related Azure services, see [Questions and answers](v4sdk/bot-builder-concept-luis.md#questions-and-answers) in the **Natural language understanding** concept article. -Search, QnA Maker and LUIS are each powerful tools in their own right, but they can -also be combined to build knowledge bots that possess more than one of those capabilities. +## To interpret intent -### LUIS and Search +Some knowledge bots require natural language processing (NLP) capabilities so that they can analyze a user's messages to determine the user's intent and other important information. -In the music festival bot example [covered earlier](#search), -the bot guides the conversation by showing buttons that represent the lineup. -However, this bot could also incorporate natural language understanding by using LUIS -to determine intent and entities within questions such as "what kind of music does Romit Girdhar play?". The bot could then search against an Azure Search index using musician name. - -It wouldn't be feasible to train the model with every possible musician name since there are so many potential values, but you could provide enough representative examples for LUIS to properly identify the entity at hand. For example, consider that you train your model by providing examples of musicians: +In a music playing bot, for example, a user might message "Play Reggae", "Play Bob Marley", or "Play One Love". +You can train a language model to map each of these messages to the intent "playMusic", without being trained with every artist, genre and song name. -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/answerGenre.png) -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/answerGenreOneWord.png) +Your language model might not understand whether the thing to play, the _entity_, is a genre, artist, or song. +However, your bot can search for that entity using this information, and proceed from there. -When you test this model with new utterances like, "what kind of music do the beatles play?", -LUIS successfully determines the intent "answerGenre" and the identifies entity "the beatles." -However, if you submit a longer question such as "what kind of music does the devil makes three play?", -LUIS identifies "the devil" as the entity. +For information about related Azure services, see [Language understanding](v4sdk/bot-builder-concept-luis.md#language-understanding) in the **Natural language understanding** concept article. -![Dialog Structure](~/media/bot-service-design-pattern-knowledge-base/devilMakesThreeScore.png) +## To integrate multiple features -By training the model with example entities that are representative of the underlying dataset, you -can increase the accuracy of your bot's language understanding. +Each NLP feature is a powerful tool in its own right. +However, your bot can combine these features and others to provide to your users a more fluid and natural experience. +Use the confidence scores to determine which feature best maps to the user's message, and ask follow-up questions if the best match is ambiguous. -> [!TIP] -> In general, it is better for the model to err by identifying excess words in its entity recognition, e.g., identify "John Smith please" from the utterance "Call John Smith please", -> rather than identify too few words, e.g., identify "John" from the utterance "Call John Smith please". -> The search index will ignore irrelevant words such as "please" in the phrase "John Smith please". +For example, such a bot can let the user: -### LUIS and QnA Maker +- Find a show they're interested in attending. +- Get information about the artist, venue, and event. +- Purchase a ticket or sign up for notices of future events. -Some knowledge bots might use QnA Maker to answer basic questions in combination with LUIS to determine intents, extract entities and invoke more elaborate dialogs. For example, consider a simple IT Help Desk bot. This bot may use QnA Maker to answer basic questions about Windows or Outlook, but it might also need to facilitate scenarios like password reset, which require intent recognition and back-and-forth communication between user and bot. There are a few ways that a bot may implement a hybrid of LUIS and QnA Maker: +For information about related Azure services, see [Use multiple features together](v4sdk/bot-builder-concept-luis.md#use-multiple-features-together) in the **Natural language understanding** concept article. -1. Call both QnA Maker and LUIS at the same time, and respond to the user by using information from the first one that returns a score of a specific threshold. -2. Call LUIS first, and if no intent meets a specific threshold score, i.e., "None" intent is triggered, then call QnA Maker. Alternatively, create a LUIS intent for QnA Maker, feeding your LUIS model with example QnA questions that map to "QnAIntent." -3. Call QnA Maker first, and if no answer meets a specific threshold score, then call LUIS. +## Explore samples -The Bot Framework SDK provide built-in support for LUIS and QnA Maker. This enables you to trigger dialogs or automatically answer questions using LUIS and/or QnA Maker without having to implement custom calls to either tool. See the [Dispatch Tool Tutorial](https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-dispatch?view=azure-bot-service-4.0) for more information. +The [Bot Framework Samples](https://github.com/microsoft/BotBuilder-Samples#readme) repo has a few sample bots that demonstrate language understanding features: -> [!TIP] -> When implementing a combination of LUIS, QnA Maker, and/or Azure Search, -> test inputs with each of the tools to determine the threshold score for each of your models. -> LUIS, QnA Maker, and Azure Search each generate scores by using a different scoring criteria, so -> the scores generated across these tools are not directly comparable. -> Additionally, LUIS and QnA Maker normalize scores. A certain score may be considered 'good' -> in one LUIS model but not so in another model. +| Sample | Sample Name | Description | +| ------ | -------------------- | ------------------------------------------------------------------------------ | +| 11 | QnA Maker (simple) | Answer questions as a series of _single-turn_ conversations using QnA Maker. | +| 13 | Core bot | Interpret the user's intent using LUIS. | +| 14 | NLP with dispatch | Dispatch user messages to LUIS or QnA Maker using Orchestrator. | +| 49 | QnA Maker (advanced) | Answer questions using multi-turn and active learning features in QnA Maker. | + +[!INCLUDE [qnamaker-sunset-alert](includes/qnamaker-sunset-alert.md)] -## Sample code +[!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] -- For a sample that shows how to create a basic knowledge bot using the Bot Framework SDK for .NET, see the Knowledge Bot sample in GitHub. - +The [Azure SDK for .NET](https://github.com/Azure/azure-sdk-for-net#readme) and [Azure SDK for Python](https://github.com/Azure/azure-sdk-for-python#readme) repositories also have a few samples: -[qnamakerTemplate]: https://docs.botframework.com/azure-bot-service/templates/qnamaker/#navtitle +| Feature | Samples README | +|:-|:-| +| Question answering | [C#](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-questionanswering/samples#readme), [Python](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/cognitivelanguage/Azure.AI.Language.QuestionAnswering/samples#readme) | +| Conversational language understanding, orchestration workflow | [C#](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/cognitivelanguage/Azure.AI.Language.QuestionAnswering/samples#readme), [Python](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/cognitivelanguage/azure-ai-language-conversations/samples#readme) | diff --git a/articles/bot-service-design-pattern-task-automation.md b/articles/bot-service-design-pattern-task-automation.md deleted file mode 100644 index 654780d5e..000000000 --- a/articles/bot-service-design-pattern-task-automation.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Create task automation bots - Bot Service -description: Learn how to design bots that perform tasks without further human intervention. -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/13/2018 -monikerRange: 'azure-bot-service-3.0' ---- - -# Create task automation bots - -[!INCLUDE [pre-release-label](./includes/pre-release-label-v3.md)] - -A task automation bot enables the user to complete a specific task or set of tasks without any assistance from a human. -This type of bot often closely resembles a typical app or website, communicating with the user primarily via rich user controls and text. -It may have natural language understanding capabilities to enrich conversations with users. - -## Example use case: password-reset - -To better understand the nature of a task bot, consider an example use case: password-reset. - -The Contoso company receives several help desk calls each day from employees who need to reset their passwords. Contoso wants to automate the simple, repeatable task of resetting a employee's password so that help desk agents can devote their time to addressing more complex issues. - -John, an experienced developer from Contoso, decides to create a bot to automate the password-reset task. -He begins by writing a design specification for the bot, just as he would do if he were creating a new app or website. - -### Navigation model - -The specification defines the navigation model: - -![Dialog Structure](~/media/bot-service-design-pattern-task-automation/simple-task1.png) - -The user begins at the `RootDialog`. When they request a password reset, they -will be directed to the `ResetPasswordDialog`. -With the `ResetPasswordDialog`, the bot will prompt the user for two pieces of information: phone number and birth date. - -> [!IMPORTANT] -> The bot design described in this article is intended for example purposes only. -> In real-world scenarios, a password-reset bot would likely implement a more robust identity verification process. - -### Dialogs - -Next, the specification describes the appearance and functionality of each dialog. - -#### Root dialog - -The root dialog provides the user with two options: - -1. **Change Password** is for scenarios where the user knows their current password and simply wants to change it. -2. **Reset Password** is for scenarios where the user has forgotten or misplaced their password and needs to generate a new one. - -> [!NOTE] -> For simplicity, this article describes only the **reset password** flow. - -The specification describes the root dialog as shown in the following screenshot. - -![Dialog Structure](~/media/bot-service-design-pattern-task-automation/simple-task2.png) - -#### ResetPassword dialog - -When the user chooses **Reset Password** from the root dialog, the `ResetPassword` dialog is invoked. -The `ResetPassword` dialog then invokes two other dialogs. -First, it invokes the `PromptStringRegex` dialog to collect the user's phone number. -Then it invokes the `PromptDate` dialog to collect the user's date of birth. - -> [!NOTE] -> In this example, John chose to implement the logic for collecting the user's phone number -> and date of birth by using two separate dialogs. -> The approach not only simplifies the code required for each dialog, but also increases the odds of these -> dialogs being usable by other scenarios in the future. - -The specification describes the `ResetPassword` dialog. - -![Dialog Structure](~/media/bot-service-design-pattern-task-automation/simple-task3.png) - -#### PromptStringRegex dialog - -The `PromptStringRegex` dialog prompts the user to enter their phone number, and verifies that the phone number -that the user provides matches the expected format. -It also accounts for the scenario where the user repeatedly provides invalid input. -The spec describes the `PromptStringRegex` dialog. - -![Dialog Structure](~/media/bot-service-design-pattern-task-automation/simple-task4.png) - -### Prototype - -Finally, the spec provides an example of a user communicating with the bot to successfully complete the password-reset task. - -![Dialog Structure](~/media/bot-service-design-pattern-task-automation/simple-task5.png) - -## Bot, app, or website? - -You may be wondering, if a task automation bot closely resembles an app or website, why not just build an app or website instead? -Depending on your particular scenario, building an app or website instead of a bot may be an entirely reasonable choice. -You may even choose to embed your bot into an app, by using the [Bot Framework Direct Line API][directLineAPI] -or Web Chat control. -Implementing your bot within the context of an app provides the best of both worlds: a rich app experience and a conversational experience, all in one place. - -In many cases, however, building an app or website can be significantly more complex and more expensive than building a bot. -An app or website often needs to support multiple clients and platforms, packaging and deploying -can be tedious and time-consuming processes, and the user experience of having to download and install an app is not necessarily ideal. -For these reasons, a bot may often provide a much simpler way of solving the problem at hand. - -Additionally, bots provide the freedom to easily expand and extend. -For example, a developer may choose to add natural language and speech capabilities to the password-reset bot so that it can be accessed via audio call, -or she may add support for text messages. -The company may setup kiosks throughout the building and embed the password-reset bot into that experience. - - -## Additional resources - -- [Dialogs](~/dotnet/bot-builder-dotnet-dialogs.md) -- [Manage conversation flow with dialogs (.NET)](~/dotnet/bot-builder-dotnet-manage-conversation-flow.md) -- [Manage conversation flow with dialogs (Node.js)](~/nodejs/bot-builder-nodejs-manage-conversation-flow.md) - - -[directLineAPI]: https://docs.botframework.com/restapi/directline3/#navtitle diff --git a/articles/bot-service-design-principles.md b/articles/bot-service-design-principles.md index e3c748926..fbbc5693d 100644 --- a/articles/bot-service-design-principles.md +++ b/articles/bot-service-design-principles.md @@ -1,73 +1,75 @@ --- -title: Principles of bot design - Bot Service -description: Learn what makes a good conversational bot and how to plan and design bots to fit your needs and delight your users. -keywords: best practices, bot design -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 - +title: Conversational user experience in the Bot Framework SDK +description: Learn what makes a great conversational user experience and how to design bots that delight your users. +keywords: conversational user experience, design guide, best practices, bot design +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: quvanwal +ms.service: azure-ai-bot-service +ms.topic: overview +ms.custom: + - evergreen --- -# Principles of bot design +# Conversational user experience -The Bot Framework enables developers to create compelling bot experiences that solve a variety of business problems. By learning the concepts described in this section, you'll become equipped to design a bot that aligns with best practices and capitalizes on lessons learned thus far in this relatively new arena. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -## Designing a bot +The Bot Framework enables developers to create conversational bots, virtual agents, digital assistants, and all other dialog interfaces—offering flexible, accessible, and powerful ways to connect with customers, employees, and one another. +Whether you're a novice or a veteran developer, the concepts described in this section will offer insights to help you craft an effective, responsible, inclusive, and, we hope, delightful experience that tackles various business scenarios. -If you are building a bot, it's safe to assume that you are expecting users to use it. -It is also safe to assume that you are hoping that users will prefer the bot experience over alternative experiences like apps, websites, phone calls, and other means of addressing their particular needs. -In other words, your bot is competing for users' time against things like apps and websites. -So, how can you maximize the odds that your bot will achieve its ultimate goal of attracting and keeping users? -It's simply a matter of prioritizing the right factors when designing your bot. +We define _conversational user experience (CUX)_ as a modality of interaction that's based on natural language. +When interacting with each other, human beings use conversation to communicate ideas, concepts, data, and emotional information. +CUX allows us to interact with our devices, apps, and digital services the way we communicate with each other, using phrasing and syntax via voice and text or chat that come naturally. -## Factors that do not guarantee a bot's success +Other modalities can burden users with the task of learning interaction behaviors that are meaningful to the system: the syntax of a command line, the information architecture of a graphical user interface, or the touch affordances of a device. +CUX turns the tables. +Instead of users having to learn the system, it's the system that learns. +It learns what we teach it about human language—patterns of speech, colloquialisms, chit-chat, even abusive words—so that it can respond appropriately. -When designing your bot, be aware that none of the following factors necessarily guarantee a bot's success: +## A great conversational bot -- **How “smart” the bot is**: -In most cases, it is unlikely that making your bot smarter will guarantee happy users or adoption of your platform. In reality, many bots have little advanced machine learning or natural language capabilities. A bot may include those capabilities if they're necessary to solve the problems that it's designed to address, however you should not assume any correlation between a bot's intelligence and user adoption of the bot. +Most successful bots have at least one thing in common: a great conversational user experience. +CUX can be multi-modal—employing text or voice, with or without visual, auditory, or touch enabled components. +But fundamentally, CUX is human language. -- **How much natural language the bot supports**: -Your bot can be great at conversations. -It can have a vast vocabulary and can even make great jokes. -But unless it addresses the problems that your users need to solve, these capabilities may contribute very little to making your bot successful. -In fact, some bots have no conversational capability at all. And in many cases, that's perfectly fine. - -- **Voice**: -It isn’t always the case that enabling bots for speech will lead to great user experiences. -Often, forcing users to use voice can result in a frustrating user experience. -As you design your bot, always consider whether voice is the appropriate channel for the given problem. -Is there going to be a noisy environment? -Will voice convey the information that needs to be shared with the user? - -## Factors that do influence a bot's success +> [!TIP] +> Regardless of the type of bot you're creating, make CUX a top priority. -Most successful apps or websites have at least one thing in common: a great user experience. -Bots are no different in that regard. -Therefore, ensuring a great user experience should be your number one priority when designing a bot. +If you're designing a bot, assume that users will prefer the bot experience over alternative experiences like apps, websites, phone calls with live agents, and other means of addressing their particular queries. +Therefore, ensuring a great conversational user experience should be your number one priority when designing a bot. Some key considerations include: -- Does the bot easily solve the user’s problem with the minimum number of steps? - -- Does the bot solve the user’s problem better/easier/faster than any of the alternative experiences? - +- Does the bot easily solve the user's problem with minimal back and forth turns? +- Does the bot solve the user's problem better/easier/faster than any of the alternative experiences? - Does the bot run on the devices and platforms the user cares about? +- Is the bot discoverable and easy to invoke? +- Does the bot guide the user when they're stuck; either via handover to a live agent or by providing relevant help? + +Users care when the bot solves their query. A great conversational bot doesn't require users to type too much, talk too much, repeat themselves several times, or explain things that the bot should automatically know and remember. -- Is the bot discoverable? Do the users naturally know what to do when using it? +## The CUX guide -None of these questions directly relates to factors such as how smart the bot is, how much natural language capability it has, whether it uses machine learning, or which programming language was used to create it. Users are unlikely to care about any of these things if the bot solves the problem that they need to address and delivers a great user experience. A great bot user experience does not require users to type too much, talk too much, repeat themselves several times, or explain things that the bot should automatically know. +The CUX guide, contains guidance on designing a bot. This guidance aligns with best practices and capitalizes on lessons learned. +The authors and designers of this guidance are drawing from combined decades of experience building and deploying conversational UX for various types of bots, virtual agents, and other conversational experience projects, including Bot Framework Templates, Microsoft Virtual Assistant, Personality Chat, and others. > [!TIP] -> Regardless of the type of application you're creating (bot, website, or app), make user experience a top priority. +> Download the [CUX Guide Microsoft.pdf](https://github.com/microsoft/botframework-sdk/raw/main/docs/CUX%20Guide%20Microsoft.pdf) + +This CUX guide is divided loosely into a few different sections. The CUX guide includes: + +- An introduction to CUX, ethics and inclusive design. +- A brainstorming worksheet and guidelines for planning and designing. +- Practical development tips for building CUX experiences. -The process of designing a bot is like the process of designing an app or website, so -the lessons learned from decades of building UI and delivering UX for apps and websites still apply when it comes to designing bots. +Read the topics in order, or jump to the area that addresses your needs. -Whenever you are unsure about the right design approach for your bot, step back and ask yourself the following question: how would you solve that problem in an app or a website? Chances are, the same answer can be applied to bot design. +> [!NOTE] +> A note on terminology: the guide explores several kinds of conversational experiences, including bots, virtual agents, and digital assistants. +We use those terms relatively interchangeably because the principles of CUX design in this guidance apply to all, but we recognize there are distinctions in the industry. +> Our intention is to offer guidance that will help with most text-based conversational experiences, regardless of their intent. ## Next steps -Now that you're familiar with some basic principles of bot design, learn more about the basic principles for [designing the first interaction](~/bot-service-design-first-interaction.md) between a user and bot. +Now that you're familiar with conversational user experiences, learn more about [designing the first interaction](bot-service-design-first-interaction.md). diff --git a/articles/bot-service-design-user-experience.md b/articles/bot-service-design-user-experience.md index d1beb624e..fb737ce76 100644 --- a/articles/bot-service-design-user-experience.md +++ b/articles/bot-service-design-user-experience.md @@ -1,106 +1,107 @@ --- -title: Design the user experience - Bot Service +title: Design the user experience description: Learn how to design your bot to deliver an engaging user experience, by using rich user controls, natural language understanding, and speech. -keywords: overview, design, user experience, UX, rich user control -author: matvelloso -ms.author: mateusv -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 09/20/2018 - +keywords: overview, design, user experience, UX, rich user control +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Design the user experience -You can create bots with a variety of features such as text, buttons, images, rich cards displayed in carousel or list format, and more. However, each channel such as Facebook, Slack, Skype, etc. ultimately controls how its messaging clients render features. Even when multiple channels support a feature, each channel may render the feature in a slightly different way. In cases where a message contains feature(s) that a channel does not natively support, the channel may attempt to down-render message contents as text or as a static image, which can significantly impact the message's appearance on the client. In some cases, a channel may not support a particular feature at all. For example, GroupMe clients cannot display a typing indicator. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +You can create bots with various features such as text, buttons, images, rich cards displayed in carousel or list format, and more. However, each channel, such as Facebook, Slack, and so on, ultimately controls how its messaging clients render features. Even when multiple channels support a feature, each channel may render the feature in a slightly different way. In cases where a message contains feature(s) that a channel doesn't natively support, the channel may attempt to down-render message contents as text or as a static image, which can significantly impact the message's appearance on the client. In some cases, a channel may not support a particular feature at all. For example, GroupMe clients can't display a typing indicator. ## Rich user controls -**Rich user controls** are common UI controls such as buttons, images, carousels, and menus that the bot presents to the user and the user engages with to communicate choice and intent. A bot can use a collection of UI controls to mimic an app, or can even run embedded within an app. When a bot is embedded within an app or website, it can represent virtually any UI control by leveraging the capabilities of the app that is hosting it. +_Rich user controls_ are common UI controls such as buttons, images, carousels, and menus that the bot presents to the user and the user engages with to communicate choice and intent. A bot can use a collection of UI controls to mimic an app, or can even run embedded within an app. When a bot is embedded within an app or website, it can represent virtually any UI control, using the capabilities of the app that is hosting it. -For decades, application and website developers have relied on UI controls to enable users to interact with their applications and these same UI controls can also be very effective in bots. For instance, buttons are a great way to present the user with a simple choice. Allowing the user to communicate "Hotels" by clicking a button labeled **Hotels** is easier and quicker than forcing the user to type "Hotels." This especially holds true on mobile devices, where clicking is greatly preferred over typing. +Application and website developers have relied on UI controls to enable users to interact with their applications. These same UI controls can also be effective in bots. For instance, buttons are a great way to present the user with a simple choice. Allowing the user to communicate "Hotels" by selecting a button labeled **Hotels** is easier and quicker than forcing the user to type "Hotels." On mobile devices, for instance, selecting is often preferred over typing. ## Cards -Cards allow you to present your users with a variety of visual, audio, and/or selectable messages and help to assist conversation flow. If a user needs to select from within a fixed set of items you can display a carousel of cards, each containing an image, a text description, and a single selection button. If a user has a set of choices for a single item, you can present a smaller single image and a collection of buttons with various options to choose between. Did they ask for more information on a subject? Cards can provide in depth information using audio or video output, or a receipt that details their shopping experience. There is an incredibly wide range uses for cards to help guide the conversation between your user and your bot. The type of card you use will be determined by the needs of your application. Let’s take a closer look at cards, their actions, and some recommended uses. +Cards allow you to present your users with various visual, audio, and/or selectable messages and help to assist conversation flow. If a user needs to select from within a fixed set of items you can display a carousel of cards, each containing an image, a text description, and a single selection button. If a user has a set of choices for a single item, you can present a smaller single image and a collection of buttons with various options to choose between. Did they ask for more information on a subject? Cards can provide in depth information using audio or video output, or a receipt that details their shopping experience. There's an incredibly wide range of uses for cards to help guide the conversation between your user and your bot. The type of card you use will be determined by the needs of your application. Let's take a closer look at cards, their actions, and some recommended uses. -Microsoft Bot Service cards are programmable objects containing standardized collections of rich user controls that are recognized across a wide range of channels. The following table describes the list of available cards and best practice suggestions of usage for each type of card. +Azure AI Bot Service cards are programmable objects containing standardized collections of rich user controls that are recognized across a wide range of channels. The following table describes the list of available cards and best practice suggestions of usage for each type of card. | Card type | Example | Description | -| ---- | ---- | ---- | -| AdaptiveCard | ![Adaptive card Image](./media/adaptive-card.png) | An open card exchange format rendered as a JSON object. Typically used for cross-channel deployment of cards. Cards adapt to the look and feel of each host channel. | -| AnimationCard | ![Animation card Image](./media/animation-card1.png) | A card that can play animated GIFs or short videos. | -| AudioCard | ![Audio card Image](./media/audio-card.png) | A card that can play an audio file. | -| HeroCard | ![Hero card Image](./media/hero-card1.png) | A card that contains a single large image, one or more buttons, and text. Typically used to visually highlight a potential user selection. | -| ThumbnailCard | ![Thumbnail card Image](./media/thumbnail-card.png) | A card that contains a single thumbnail image, one or more buttons, and text. Typically used to visually highlight the buttons for a potential user selection. | -| ReceiptCard | ![Receipt card Image](./media/receipt-card1.png) | A card that enables a bot to provide a receipt to the user. It typically contains the list of items to include on the receipt, tax and total information, and other text. | -| SignInCard | ![Sign-in card Image](./media/sign-in-card.png) | A card that enables a bot to request that a user sign-in. It typically contains text and one or more buttons that the user can click to initiate the sign-in process. | -| SuggestedAction | ![Suggested actions card Image](./media/suggested-actions.png) | Presents your user with a set of CardActions representing a user choice. This card disappears once any of the suggested actions is selected. | -| VideoCard | ![Video card Image](./media/video-card.png) | A card that can play videos. Typically used to open a URL and stream an available video. | -| CardCarousel | ![Card carousel Image](./media/card-carousel.png) | A horizontally scrollable collection of cards that allows your user to easily view a series of possible user choices.| - -Cards allow you to design your bot once, and have it work across a variety of channels. However, not all card types are fully supported across all available channels. - -Detailed instructions for adding cards to your bot can be found within these sections [Add rich card media attachments](v4sdk/bot-builder-howto-add-media-attachments.md) and [Add suggested actions to messages](v4sdk/bot-builder-howto-add-suggested-actions.md). Sample code can also be found here for cards: [C#](https://aka.ms/bot-cards-sample-code-cs)/[JS](https://aka.ms/bot-cards-sample-code-js) adaptive cards: [C#](https://aka.ms/bot-adaptive-cards-sample-code)/[JS](https://aka.ms/bot-adaptive-cards-js-sample-code), attachments: [C#](https://aka.ms/bot-attachments-sample-code)/[JS](https://aka.ms/bot-attachments-js-sample-code), and suggested actions: [C#](https://aka.ms/bot-suggested-actions-code)/[JS](https://aka.ms/bot-suggested-actions-js-code). - - - -When designing your bot, do not automatically dismiss common UI elements as not being "smart enough." As discussed [previously](~/bot-service-design-principles.md#designing-a-bot), your bot should be designed to solve the user's problem in the best, quickest, and easiest manner possible. Avoid the temptation to start by incorporating natural language understanding, as it is often unnecessary and introduces unjustified complexity. +|--|--|--| +| AdaptiveCard | :::image type="content" source="./media/adaptive-card.png" alt-text="Image of an Adaptive Card."::: | An open card exchange format rendered as a JSON object. Typically used for cross-channel deployment of cards. Cards adapt to the look and feel of each host channel. | +| AnimationCard | :::image type="content" source="./media/animation-card1.png" alt-text="Image of an animation card."::: | A card that can play animated GIFs or short videos. | +| AudioCard | :::image type="content" source="./media/audio-card.png" alt-text="Image of an audio card."::: | A card that can play an audio file. | +| HeroCard | :::image type="content" source="./media/hero-card1.png" alt-text="Image of a hero card."::: | A card that contains a single large image, one or more buttons, and text. Typically used to visually highlight a potential user selection. | +| ThumbnailCard | :::image type="content" source="./media/thumbnail-card.png" alt-text="Image of a thumbnail card."::: | A card that contains a single thumbnail image, one or more buttons, and text. Typically used to visually highlight the buttons for a potential user selection. | +| ReceiptCard | :::image type="content" source="./media/receipt-card1.png" alt-text="Image of a receipt card."::: | A card that enables a bot to provide a receipt to the user. It typically contains the list of items to include on the receipt, tax and total information, and other text. | +| SignInCard | :::image type="content" source="./media/sign-in-card.png" alt-text="Image of a sign-in card."::: | A card that lets a user sign in. It typically contains text and one or more buttons that the user can use to initiate a sign-in process. | +| SuggestedAction | :::image type="content" source="./media/suggested-actions.png" alt-text="Image of suggested actions rendered as buttons within a chat."::: | Presents your user with a set of _card actions_ that represent a user choice. The buttons disappear once any of the suggested actions is selected. | +| VideoCard | :::image type="content" source="./media/video-card.png" alt-text="Image of a video card."::: | A card that can play videos. Typically used to open a URL and stream an available video. | +| CardCarousel | :::image type="content" source="./media/card-carousel.png" alt-text="Image of a card carousel."::: | A horizontally scrollable collection of cards that allows your user to easily view a series of possible user choices. | + +Cards allow you to design your bot once, and have it work across various channels. However, not all card types are fully supported across all available channels. + +- Detailed instructions for adding cards to your bot can be found in [Add rich card media attachments](v4sdk/bot-builder-howto-add-media-attachments.md) and [Add suggested actions to messages](v4sdk/bot-builder-howto-add-suggested-actions.md). +- For sample code, see the following sample bots in the [Bot Framework Samples](https://github.com/microsoft/BotBuilder-Samples#readme) repo. + + | Sample | Name | Description | + |-------:|:------------------|:------------------------------------------------------| + | 6 | Using cards | Demonstrates use of all card types. | + | 7 | Adaptive Cards | Demonstrates use of Adaptive Cards. | + | 8 | Suggested actions | Demonstrates use of suggested actions. | + | 15 | Attachments | Demonstrates how to accept user provided attachments. | + +When designing your bot, don't automatically dismiss common UI elements as not being _smart enough_. As discussed in [Conversational user experience](bot-service-design-principles.md#a-great-conversational-bot), your bot should be designed to solve the user's problem in the best, quickest, and easiest manner possible. Avoid the temptation to start by incorporating natural language understanding, as it's often unnecessary and introduces unjustified complexity. > [!TIP] > Start by using the minimum UI controls that enable the bot to solve the user's problem, > and add other elements later if those controls are no longer sufficient. - ## Text and natural language understanding -A bot can accept **text** input from users and attempt to parse that input using regular expression matching or **natural language understanding** APIs, such as LUIS. Depending on the type of input that the user provides, natural language understanding may or may not be a good solution. +A bot can accept text input from users and attempt to parse that input using regular expression matching or natural language understanding APIs. Depending on the type of input that the user provides, natural language understanding may or may not be a good solution. -In some cases, a user may be **answering a very specific question**. For example, if the bot asks, "What is your name?", the user may answer with text that specifies only the name, "John", or with a sentence, "My name is John". +In some cases, a bot may ask the user a specific question. For example, if the bot asks, "What is your name?", the user may answer with text that specifies only the name, "John", or with a sentence, "My name is John". -Asking specific questions reduces the scope of potential responses that the bot might reasonably receive, which decreases the complexity of the logic necessary to parse and understand the response. For example, consider the following broad, open-ended question: "How are you feeling?". Understanding the many possible permutations of potential answers to such a question is a very complex task. +Asking specific questions reduces the scope of potential responses that the bot might reasonably receive, which decreases the complexity of the logic necessary to parse and understand the response. For example, consider the following broad, open-ended question: "How are you feeling?". Understanding the many possible permutations of potential answers to such a question is a complex task. -In contrast, specific questions such as "Are you feeling pain? yes/no" and "Where are you feeling pain? chest/head/arm/leg" would likely prompt more specific answers that a bot can parse and understand without needing to implement natural language understanding. +In contrast, specific questions such as "Are you feeling pain? yes/no" and "Where are you feeling pain? chest/head/arm/leg" would likely prompt more specific answers that a bot can parse and understand without needing to implement natural language understanding. > [!TIP] -> Whenever possible, ask specific questions that will not require natural language understanding capabilities to parse the response. This will simplify your bot and increase the success your bot will understand the user. +> Whenever possible, ask specific questions that won't require natural language understanding capabilities to parse the response. This will simplify your bot and increase the success your bot will understand the user. - -In other cases, a user may be **typing a specific command**. For example, a DevOps bot that enables developers to manage virtual machines could be designed to accept specific commands such as "/STOP VM XYZ" or "/START VM XYZ". Designing a bot to accept specific commands like this makes for a good user experience, as the syntax is easy to learn and the expected outcome of each command is clear. Additionally, the bot will not require natural language understanding capabilities, since the user's input can be easily parsed using regular expressions. +In other cases, a user may type a specific command. For example, a DevOps bot that enables developers to manage virtual machines could be designed to accept specific commands such as "/STOP VM XYZ" or "/START VM XYZ". Designing a bot to accept specific commands like this makes for a good user experience, as the syntax is easy to learn and the expected outcome of each command is clear. Additionally, the bot won't require natural language understanding capabilities, since the user's input can be easily parsed using regular expressions. > [!TIP] -> Designing a bot to require specific commands from the user can often provide a good user experience while +> Designing a bot to require specific commands from the user can often provide a good user experience while > also eliminating the need for natural language understanding capability. - -In the case of a *knowledge base* bot or *questions and answers* bot, a user may be **asking general questions**. For example, imagine a bot that can answer questions based on the contents of thousands of documents. QnA Maker and Azure Search are both technologies which are designed specifically for this type of scenario. For more information, see [Design knowledge bots](bot-service-design-pattern-knowledge-base.md). +For a _knowledge base_ or _questions and answers_ bot, a user may ask general questions. For example, imagine a bot that can answer questions based on the contents of thousands of documents. [Azure AI services](/azure/ai-services/) and [Azure Search](/azure/search/) are both technologies designed specifically for this type of scenario. For more information, see [Design knowledge bots](bot-service-design-pattern-knowledge-base.md) and [Language understanding](v4sdk/bot-builder-concept-luis.md). > [!TIP] -> If you are designing a bot that will answer questions based on structured or unstructured data from -> databases, web pages, or documents, consider using technologies that are designed specifically to address this -> scenario rather than attempting to solve the problem with natural language understanding. +> If you're designing a bot that will answer questions based on structured or unstructured data from +> databases, web pages, or documents, consider using technologies designed specifically to address this +> scenario, rather than attempting to solve the problem with natural language understanding. - -In other scenarios, a user may be **typing simple requests based on natural language**. For example, a user may type "I want a pepperoni pizza" or "Are there any vegetarian restaurants within 3 miles from my house open now?". Natural language understanding APIs such as [LUIS.ai](https://www.luis.ai) are a great fit for scenarios like this. +In other scenarios, a user may type simple requests based on natural language. For example, a user may type "I want a pepperoni pizza" or "Are there any vegetarian restaurants within 3 miles from my house open now?". Natural language understanding APIs are a great fit for scenarios like this. -Using the APIs, your bot can extract the key components of the user's text to identify the user's intent. When implementing natural language understanding capabilities in your bot, set realistic expectations for the level of detail that users are likely to provide in their input. - -![how users talk](./media/bot-service-design-user-experience/buy-house.png) +Using the APIs, your bot can extract the key components of the user's text to identify the user's intent. When implementing natural language understanding capabilities in your bot, set realistic expectations for the level of detail that users are likely to provide in their input. > [!TIP] -> When building natural language models, do not assume that users will provide all the required information in their initial query. -> Design your bot to specifically request the information it requires, guiding the user to provide that information -> by asking a series of questions, if necessary. +> When building natural language models, don't assume that users will provide all the required information in their initial query. +> Design your bot to specifically request the information it requires, guiding the user to provide that information +> by asking a series of questions, if necessary. - ## Speech -A bot can use **speech** input and/or output to communicate with users. In cases where a bot is designed to support devices that have no keyboard or monitor, speech is the only means of communicating with the user. +A bot can use _speech_ input and output to communicate with users. In cases where a bot is designed to support devices that have no keyboard or monitor, speech is the only means of communicating with the user. ## Choosing between rich user controls, text and natural language, and speech -Just like people communicate with each other using a combination of gestures, voice, and symbols, bots can communicate with users using a combination of rich user controls, text (sometimes including natural language), and speech. These communication methods can be used together; you do not need to choose one over another. - -For example, imagine a "cooking bot" that helps users with recipes, where the bot may provide instructions by playing a video or displaying a series of pictures to explain what needs to be done. Some users may prefer to flip pages of the recipe or ask the bot questions using speech while they are assembling a recipe. Others may prefer to touch the screen of a device instead of interacting with the bot via speech. When designing your bot, incorporate the UX elements that support the ways in which users will likely prefer to interact with your bot, given the specific use cases that it is intended support. +Just like people communicate with each other using a combination of gestures, voice, and symbols, bots can communicate with users using a combination of rich user controls, text (sometimes including natural language), and speech. These communication methods can be used together; you don't need to choose one over another. +For example, imagine a "cooking bot" that helps users with recipes, where the bot may provide instructions by playing a video or displaying a series of pictures to explain what needs to be done. Some users may prefer to flip pages of the recipe or ask the bot questions using speech while they're assembling a recipe. Others may prefer to touch the screen of a device instead of interacting with the bot via speech. When designing your bot, incorporate the UX elements that support the ways in which users will likely prefer to interact with your bot, given the specific use cases that it's intended to support. diff --git a/articles/bot-service-encryption.md b/articles/bot-service-encryption.md new file mode 100644 index 000000000..2d286add3 --- /dev/null +++ b/articles/bot-service-encryption.md @@ -0,0 +1,124 @@ +--- +title: Azure AI Bot Service encryption for data at rest in Bot Framework SDK +description: Azure AI Bot Service protects your data by automatically encrypting it before persisting it to the cloud with Microsoft provided encryption keys. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen +--- + +# Azure AI Bot Service encryption for data at rest + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Azure AI Bot Service automatically encrypts your data when it's persisted to the cloud to protect the data and to meet organizational security and compliance commitments. + +Encryption and decryption are transparent, meaning encryption and access are managed for you. Your data is secure by default and you don't need to modify your code or applications to take advantage of encryption. + +## About encryption key management + +By default, your subscription uses Microsoft-managed encryption keys. You can manage your bot resource with your own keys called customer-managed keys. Customer-managed keys offer greater flexibility to create, rotate, disable, and revoke access controls to the data Azure AI Bot Service stores. You can also audit the encryption keys used to protect your data. + +When encrypting data, Azure AI Bot Service encrypts with two levels of encryption. In the case where customer-managed keys aren't enabled, both keys used are Microsoft-managed keys. When customer-managed keys are enabled, the data is encrypted with both the customer-managed key and a Microsoft-managed key. + +## Customer-managed keys with Azure Key Vault + +To utilize the customer-managed keys feature, you must store and manage keys in **Azure Key Vault**. You can either create your own keys and store them in a key vault, or you can use the Azure Key Vault APIs to generate keys. Your Azure Bot resource and the key vault must be in the same Microsoft Entra ID tenant, but they can be in different subscriptions. For more information about Azure Key Vault, see [What is Azure Key Vault?](/azure/key-vault/key-vault-overview). + +When using a customer-managed key, Azure AI Bot Service encrypts your data in its storage. If access to that key is revoked or the key is deleted, your bot won't be able to use Azure AI Bot Service to send or receive messages, and you won't be able to access or edit the configuration of your bot in the Azure portal. + +When you create an Azure Bot resource via the portal, Azure generates an _app ID_ and a _password_, but doesn't store them in Azure Key Vault. +You can use Key Vault with Azure AI Bot Service. For information, see [Configure the web app to connect to Key Vault](/azure/key-vault/general/tutorial-net-create-vault-azure-web-app#configure-the-web-app-to-connect-to-key-vault). For an example on how to store and retrieve secrets with Key Vault, see [Quickstart: Azure Key Vault secret client library for .NET (SDK v4)](/azure/key-vault/secrets/quick-create-net). + +> [!IMPORTANT] +> The Azure AI Bot Service team can't recover a customer-managed encryption key bot without access to the key. + +## What data is encrypted? + +Azure AI Bot Service stores customer data about the bot, the channels it uses, configuration settings the developer sets, and where necessary, a record of currently active conversations. It also transiently, for less than 24 hours, stores the messages sent over the Direct Line or Web Chat channels and any attachments uploaded. + +All customer data is encrypted with two layers of encryption in Azure AI Bot Service; either with Microsoft managed encryption keys, or Microsoft and customer-managed encryption keys. Azure AI Bot Service encrypts transiently stored data using the Microsoft-managed encryption keys, and, depending on the configuration of the Azure Bot resource, encrypts longer-term data using either the Microsoft or customer-managed encryption keys. + +> [!NOTE] +> As Azure AI Bot Service exists to provide customers the ability to deliver messages to and from users on other services outside Azure AI Bot Service, encryption doesn't extend to those services. This means that while under Azure AI Bot Service control, data will be stored encrypted as per the guidance in this article; however, when leaving the service to deliver to another service, the data is decrypted and then sent using TLS 1.2 encryption to the target service. + +## How to configure your Azure Key Vault instance + +Using customer-managed keys with Azure AI Bot Service requires that you enable two properties on the Azure Key Vault instance you plan to use to host your encryption keys: **Soft delete** and **Purge protection**. These features ensure that if for some reason your key is accidentally deleted, you can recover it. For more information about soft delete and purge protection, see the [Azure Key Vault soft-delete overview](/azure/key-vault/general/soft-delete-overview). + +:::image type="content" source="media/key-vault/encryption-settings.png" alt-text="Screenshot of soft delete and purge protection enabled."::: + +If you're using an existing Azure Key Vault instance, you can verify that these properties are enabled by looking at the **Properties** section on the Azure portal. If any of these properties aren't enabled, see the **Key Vault** section in [How to enable soft delete and purge protection](/azure/key-vault/general/key-vault-recovery). + +### Grant Azure AI Bot Service access to a key vault + +For Azure AI Bot Service to have access to the key vault you created for this purpose, an access policy needs to be set, which gives Azure AI Bot Service's service principal the current set of permissions. For more information about Azure Key Vault, including how to create a key vault, see [About Azure Key Vault](/azure/key-vault/general/overview). + +1. Register the Azure AI Bot Service resource provider on your subscription containing the key vault. + 1. Go to the [Azure portal](https://ms.portal.azure.com). + 1. Open the **Subscriptions** blade and select the subscription that contains the key vault. + 1. Open the **Resource providers** blade and register the **Microsoft.BotService** resource provider. + + :::image type="content" source="media/key-vault/register-resource-provider.png" alt-text="Microsoft.BotService registered as a resource provider"::: + +1. Azure Key Vault supports two permission models: Azure role-based access control (RBAC) or vault access policy. You can choose to use either permission model. Ensure that the **Firewalls and virtual networks** in the **Networking** blade of the Key Vault is set to **Allow public access from all networks** at this step. Additionally, ensure that the operator has been granted the Key Management Operations permission. + + :::image type="content" source="media/key-vault/choose-permission-model.png" alt-text="Screenshot of the two permission models available for your key vault."::: + + 1. To configure the Azure RBAC permission model on your key vault: + 1. Open the **Key vaults** blade and select your key vault. + 2. Go to the **Access control (IAM)** blade, and assign the **Key Vault Crypto Service Encryption User** role to **Bot Service CMEK Prod**. Only a user with the subscription owner role can make this change. + + :::image type="content" source="media/key-vault/key-vault-rbac.png" alt-text="Screenshot of key vault configuration showing the crypto service encryption user role has been added."::: + + 1. To configure the Key Vault access policy permission model on your key vault: + 1. Open the **Key vaults** blade and select your key vault. + 1. Add the **Bot Service CMEK Prod** application as an access policy, and assign it the following permissions: + - **Get** (from the **Key Management Operations**) + - **Unwrap Key** (from the **Cryptographic Operations**) + - **Wrap Key** (from the **Cryptographic Operations**) + 1. Select **Save** to save any changes you made. + + :::image type="content" source="media/key-vault/access-policies.png" alt-text="Bot Service CMEK Prod added as an access policy"::: + +1. Allow Key Vault to bypass your firewall. + 1. Open the **Key vaults** blade and select your key vault. + 1. Open the **Networking** blade and go to the **Firewalls and virtual networks** tab. + 1. If **Allow access from** is set to **Disable public access**, make sure **Allow trusted Microsoft services to bypass this firewall** is selected. + 1. Select **Save** to save any changes you made. + + :::image type="content" source="media/key-vault/firewall-exception.png" alt-text="Firewall exception added for Key Vault"::: + +### Enable customer-managed keys + +To encrypt your bot with a customer-managed encryption key, follow these steps: + +1. Open the Azure Bot resource blade for your bot. +1. Open the **Encryption** blade of your bot and select **Customer-Managed Keys** for the **Encryption type**. +1. Either input your key's complete URI, including version, or click **Select a key vault and a key** to find your key. +1. Click **Save** at the top of the blade. + + :::image type="content" source="media/key-vault/customer-managed-encryption.png" alt-text="Bot resource using customer-managed encryption"::: + +Once these steps are completed, Azure AI Bot Service will start the encryption process, which can take up to 24 hours to complete. Your bot remains functional during this time period. + +### Rotate customer-managed keys + +To rotate a customer-managed encryption key, you must update the Azure AI Bot Service resource to use the new URI for the new key (or new version of the existing key). + +Because re-encryption with the new key occurs asynchronously, ensure the old key remains available so that data can continue to be decrypted; otherwise, your bot could stop working. You should retain the old key for at least one week. + +### Revoke access to customer-managed keys + +To revoke access, remove the access policy for the **Bot Service CMEK Prod** service principal from your key vault. + +> [!NOTE] +> Revoking access will break most of the functionality associated with your bot. To disable the customer-managed keys feature, turn off the feature before revoking access to ensure the bot can continue working. + +## Next steps + +Learn more [About Azure Key Vault](/azure/key-vault/key-vault-overview) diff --git a/articles/bot-service-manage-analytics.md b/articles/bot-service-manage-analytics.md deleted file mode 100644 index 7ac02008e..000000000 --- a/articles/bot-service-manage-analytics.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: Bot analytics - Bot Service -description: Learn how to use data collection and analysis to improve your bot with analytics in the Bot Framework. -keywords: bot analytics, application insights, traffic, latency, integrations, AppInsights -author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/04/2018 ---- - -# Bot analytics - -Analytics is an extension of [Application Insights](/azure/application-insights/app-insights-analytics). Application Insights provides **service-level** and instrumentation data like traffic, latency, and integrations. Analytics provides **conversation-level** reporting on user, message, and channel data. - -## View analytics for a bot - -To access Analytics, open the bot in the Azure portal and click **Analytics**. - -Too much data? You can [enable and configure sampling](/azure/application-insights/app-insights-sampling) for the Application Insights linked to your bot. This reduces telemetry traffic and storage while maintaining statistically correct analysis. - -### Specify channel - -Choose which channels appear in the graphs below. Note that if a bot is not enabled on a channel, there will be no data from that channel. - -![Select channel](~/media/analytics-channels.png) - -* Check the check box to include a channel in the chart. -* Clear the check box to remove a channel from the chart. - -### Specify time period - -Analysis is available for the past 90 days only. Data collection began when Application Insights was enabled. - -![Select time period](~/media/analytics-timepick.png) - -Click the drop-down menu and then click the amount of time the graphs should display. -Note that changing the overall time frame will cause the time increment (X-axis) on the graphs to change accordingly. - -### Grand totals - -The total number of active users and activities sent and received during the specified time frame. -Dashes `--` indicate no activity. - -### Retention - -Retention tracks how many users who sent one message came back later and sent another one. -The chart is a rolling 10-day window; the results are not affected by changing the time frame. - -![Retention chart](~/media/analytics-retention.png) - -Note that the most recent possible date is two days ago; a user sent messages the day before yesterday and then *returned* yesterday. - -### User - -The Users graph tracks how many users accessed the bot using each channel during the specified time frame. - -![Users graph](~/media/analytics-users.png) - -* The percentage chart shows what percentage of users used each channel. -* The line graph indicates how many users were accessing the bot at a certain time. -* The legend for the line graph indicates which color represents which channel and the includes the total number of users during the specified time period. - -### Activities - -The Activities graph tracks how many activities were sent and received using which channel during the specified time frame. - -![activities graph](~/media/analytics-activities.png) - -* The percentage chart shows what percentage of activities were communicated over each channel. -* The line graph indicates how many activities were sent and received over the specified time frame. -* The legend for the line graph indicates which line color represents each channel and the total number of activities sent and received on that channel during the specified time period. - -## Enable analytics - -Analytics are not available until Application Insights has been enabled and configured. Application Insights will begin collecting data as soon as it is enabled. For example, if Application Insights was enabled a week ago for a six-month-old bot, it will have collected one week of data. - -> [!NOTE] -> Analytics requires both an Azure subscription and Application Insights [resource](/azure/application-insights/app-insights-create-new-resource). -To access Application Insights, open the bot in the [Azure Portal](https://portal.azure.com/) and click **Settings**. - -You can add Application Insights when you create your bot resource. - -You can also create an Application Insights resource later and connect it to your bot. - -1. Create an Application Insights [resource](/azure/application-insights/app-insights-create-new-resource). -2. Open the bot in the dashboard. Click **Settings** and scroll down to the **Analytics** section. -3. Enter the information to connect the bot to Application Insights. All fields are required. - -![Connect Insights](~/media/analytics-enable.png) - - - -For more information on how to locate these values, see [Application Insights keys](~/bot-service-resources-app-insights-keys.md). - -## Additional resources -* [Application Insights keys](~/bot-service-resources-app-insights-keys.md) \ No newline at end of file diff --git a/articles/bot-service-manage-channels.md b/articles/bot-service-manage-channels.md index a4a5a9f8d..d18cad6ea 100644 --- a/articles/bot-service-manage-channels.md +++ b/articles/bot-service-manage-channels.md @@ -1,40 +1,104 @@ --- -title: Configure a bot to run on one or more channels - Bot Service -description: Learn how to configure a bot to run on one or more channels using the Bot Framework Portal. -keywords: bot channels, configure, cortana, facebook messenger, kik, slack, skype, azure portal -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 09/22/2018 +title: Configure an Azure AI Bot Service bot to run on one or more channels +description: A channel connects a communication application to a bot. Learn how to configure a bot to run a channel using the Azure portal, Direct Line, or a custom adapter. +keywords: bot, channel, Azure portal, Direct Line, custom adapter +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - abs-meta-21q1 + - evergreen +monikerRange: 'azure-bot-service-4.0' --- -# Connect a bot to channels +# Configure a bot to run on one or more channels -A channel is a connection between the bot and communication apps. You configure a bot to connect to the channels you want it to be available on. The Bot Framework Service, configured through the Azure portal, connects your bot to these channels and facilitates communication between your bot and the user. You can connect to many popular services, such as [Cortana](bot-service-channel-connect-cortana.md), [Facebook Messenger](bot-service-channel-connect-facebook.md), [Kik](bot-service-channel-connect-kik.md), and [Slack](bot-service-channel-connect-slack.md), as well as several others. The [Web Chat](bot-service-channel-connect-webchat.md) channel is pre-configured for you. In addition to standard channels provided with the Bot Connector Service, you can also connect your bot to your own client application using [Direct Line](bot-service-channel-connect-directline.md) as your channel. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -The Bot Framework Service allows you to develop your bot in a channel-agnostic way by normalizing messages that the bot sends to a channel. This involves converting it from the bot framework schema into the channel’s schema. However, if the channel does not support all aspects of the bot framework schema, the service will try to convert the message to a format that the channel does support. For example, if the bot sends a message that contains a card with action buttons to the email channel, the connector may send the card as an image and include the actions as links in the message’s text. +A channel is a connection between a communication application and a bot. A bot, registered with Azure, uses channels to help the bot communicate with users. You can configure a bot to connect to any of the standard channels such as Alexa, Facebook Messenger, and Slack. For more information, see [Azure Bot registration](bot-service-quickstart-registration.md). You can also connect a bot to your communication application using Direct Line as the channel. For more information, see [Connect a bot to Direct Line](bot-service-channel-connect-directline.md). -For most channels, you must provide channel configuration information to run your bot on the channel. Most channels require that your bot have an account on the channel, and others, like Facebook Messenger, require your bot to have an application registered with the channel also. +The Bot Framework allows you to develop a bot in a channel-agnostic way by normalizing messages that the bot sends to a channel. -To configure your bot to connect to a channel, complete the following steps: +- The service or an adapter translates communication between the Bot Framework Activity schema and the channel's schema. +- If the channel doesn't support all aspects of the activity schema, the Bot Connector Service tries to convert the message to a format that the channel does support. For example, if the bot sends a message that contains a card with action buttons to the email channel, the connector might send the card as an image and include the actions as links in the body of the email. +- For most channels, you must provide channel configuration information to run a bot on the channel. Most channels require that a bot have an account on the channel. Others, like Facebook Messenger, require a bot to have an application registered with the channel. -1. Sign in to the Azure Portal. -2. Select the bot that you want to configure. -3. In the Bot Service blade, click **Channels** under **Bot Management**. -4. Click the icon of the channel you want to add to your bot. +To configure a bot to connect to a channel, complete the following steps: -![Connect to channels](./media/channels/connect-to-channels.png) +1. Sign in to the [Azure portal](https://portal.azure.com). +1. Select the bot that you want to configure. +1. In the left pane, select **Channels** under **Settings**. +1. In the right pane, select the icon of the channel you want to add to your bot. You may need to scroll down to see the list of all **Available Channels**. After you've configured the channel, users on that channel can start using your bot. -## Publish a bot +## Channels list -The publishing process is different for each channel. +The connection steps are different for each channel. See the related article in the table below more information. -[!INCLUDE [publishing](./includes/snippet-publish-to-channel.md)] +| Channel | Description | +|:-|:-| +| [Alexa](bot-service-channel-connect-alexa.md) | Communicate with users via Alexa custom skills. | +| [Azure Communication Services](bot-service-channel-azure-communication.md) | Use Azure Communication Services Chat to add a bot to the chat features of your website. | +| [Direct Line](bot-service-channel-directline.md) | Integrate a bot into a mobile app, web page, or other applications. | +| [Email](bot-service-channel-connect-email.md) | Enable a bot to communicate with users via Microsoft 365 email. | +| [Facebook](bot-service-channel-connect-facebook.md) | Connect a bot to both Facebook Messenger and Facebook Workplace, so that it can communicate with users on both platforms. | +| [GroupMe](bot-service-channel-connect-groupMe.md) | Configure a bot to communicate with users through GroupMe. | +| Kik | Kik no longer supports new bot development. | +| [LINE](bot-service-channel-connect-line.md) | Configure a bot to communicate with users through the LINE app. | +| [Microsoft Teams](channel-connect-teams.md) | Configure a bot to communicate with users through Microsoft Teams. | +| [Omnichannel](bot-service-channel-omnichannel.md) | Integrate a bot to start a conversation with a customer, provide automated responses, and then shift the conversation to a human agent if required. | +| [Outlook (preview)](bot-service-channel-connect-actionable-email.md) | Configure a bot to communicate with users via quick actions from within Outlook. | +| [Search (preview)](bot-service-channel-connect-search.md) | Enable a bot to answer user queries via Dynamics 365 federated search. | +| Skype | Skype no longer supports new bot development. | +| [Slack](bot-service-channel-connect-slack.md) | Configure a bot to communicate with users through Slack. | +| [Telegram](bot-service-channel-connect-telegram.md) | Configure a bot to communicate with users through Telegram. | +| Telephony | Telephony is in preview and isn't currently accepting additional customers. | +| [Twilio (SMS)](bot-service-channel-connect-twilio.md) | Configure a bot to communicate with users through the Twilio cloud communication platform. | +| [WeChat](bot-service-channel-connect-wechat.md) | Configure a bot to communicate with users using the WeChat platform. | +| [Web Chat](bot-service-channel-connect-webchat.md) | Automatically configured for you when you create a bot with the Bot Framework Service. | +| [Additional channels](bot-service-channel-additional-channels.md) | Additional channels available as an adapter through [Botkit provided platforms](https://github.com/howdyai/botkit/blob/main/packages/docs/platforms/index.md) and [community repositories](https://github.com/BotBuilderCommunity/). | -## Additional resources +## Select the protocol schema transformation version + +As described above, a channel converts incoming messages from other services to the Bot Framework protocol schema. Likewise, messages sent by the bot to other services are transformed from the Bot Framework native schema to the format of these services. This process is called _schema transformation_. The Bot Framework Service maintains backward compatibility of the protocol to avoid changing the behavior of existing bots. + +Occasionally, a change in the schema transformation process needs to take place that can, potentially, change the behavior of the existing bots. An example of such a change could be any bug fix, if some of the users have taken a dependency on the existing (however erroneous) behavior. Another example of such a change would be updates or improvements in other services that would benefit bots; however adopting these updates can, potentially, change the existing behavior. + +By controlling the _schema transformation version_ of their bots, bot developers can control when (if ever) to enable new behavior. By default, newly created bots get the most recent schema transformation version. Existing bots can be upgraded to the newest version when they're ready to take advantage of the improvements introduced in this version. Any bot can be upgraded or downgraded at any time. + +You can change your bot's schema transformation version in the **Configuration** pane under **Settings**: + +:::image type="content" source="./media/channels/schema-transform-version.png" alt-text="The Schema Transformation Version field in the Configuration pane"::: + +### Supported schema transformation versions + +- **Version 1.3** + - Date introduced: May 2021 + - Changes: + - Direct Line: Remove Deserialize/Reserialize of Adaptive Cards. The content of Adaptive Cards will be passed to the client as is. + +- **Version 1.2** + - Date introduced: April 2021 + - Changes: + - Slack channel: Attachment name is used for Message Text value. + - Facebook channel: Upgrade to [Facebook Graph API v9.0](https://developers.facebook.com/docs/graph-api/changelog/version9.0/). + +- **Version 1.1** + - Date introduced: April 2021 + - Changes: + - Telegram channel: Use [MarkdownV2 syntax](https://core.telegram.org/bots/api#markdownv2-style) for all markdown. + +- **Version 1.0** + - Original version + +## Connect your bot to one or more channels + +The publishing process is different for each channel. For more information, see the article for each specific channel. + +## Next steps The SDK includes samples that you can use to build bots. Visit the [Samples repo on GitHub](https://github.com/Microsoft/BotBuilder-samples) to see a list of samples. diff --git a/articles/bot-service-manage-overview.md b/articles/bot-service-manage-overview.md index eeacd1783..ec0c124e2 100644 --- a/articles/bot-service-manage-overview.md +++ b/articles/bot-service-manage-overview.md @@ -1,56 +1,78 @@ --- -title: Manage a bot - Bot Service -description: Learn how to manage a bot through the bot service portal. +title: Manage a bot +description: Learn how to manage bots. See how to use the Azure portal to find information on activity logs, build options, debug settings, and other properties. keywords: azure portal, bot management, test in web chat, MicrosoftAppID, MicrosoftAppPassword, application settings -author: v-ducvo -ms.author: rstand -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 8/13/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: overview +ms.custom: + - evergreen --- + # Manage a bot -[!INCLUDE [applies-to-both](includes/applies-to-both.md)] +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +In your browser, navigate to the [Azure portal](https://ms.portal.azure.com/). Select your bot resource, such as an **Azure Bot**. On the navigation pane you'll see the sections described below. + +## Azure Bot resource settings -This topic explains how to manage a bot using the Azure portal. +The **Azure Bot** resource contains the settings described below. -## Bot settings overview +### General -![Bot settings overview](~/media/azure-manage-a-bot/overview.png) +At the top of the navigation pane are links for general information applicable to a bot. -In the **Overview** blade, you can find high level information about your bot. For example, you can see your bot's **Subscription ID**, **pricing tier**, and **Messaging endpoint**. +| Link | Description | +|--|--| +| **Overview** | Contains high level information about the bot, such as a bot's **Subscription ID** and **Messaging endpoint**. On the overview pane, you can also download the bot source code. | +| **Activity log** | Provides detailed diagnostic and auditing information for Azure resources and the Azure platform they depend on. For more information, see [Overview of Azure platform logs](/azure/azure-monitor/platform/platform-logs-overview). | +| **Access control (IAM)** | Displays the access users or other security principals have to Azure resources. For more information, see [View the access a user has to Azure resources](/azure/role-based-access-control/check-access). | +| **Tags** | Displays the tags to your Azure resources, resource groups, and subscriptions to logically organize them into a taxonomy. For more information, see [Use tags to organize your Azure resources](/azure/azure-resource-manager/management/tag-resources). | -## Bot management +### Settings - You can find most of your bot's management options under the **BOT MANAGEMENT** section. Below is a list of options to help you manage your bot. +In the **Settings** section are links for most of your bot's management options. -![Bot management](~/media/azure-manage-a-bot/bot-management.png) +| Link | Description | +|--|--| +| **Bot profile** | Manages various bot profile settings, such as display name, icon, and description. | +| **Configuration** | Manages various bot settings, such as analytics, messaging endpoint, and OAuth connection settings. | +| **Channels** | Configures the channels your bot uses to communicate with users. | +| **Pricing** | Manages the pricing tier for the bot service. | +| **Test in Web Chat** | Uses the integrated Web Chat control to quickly test your bot. | +| **Encryption** | Manages your encryption keys. | +| **Properties** | Lists your bot resource properties, such as resource ID, subscription ID, and resource group ID. | +| **Locks** | Manages your resource locks. | -| Option | Description | -| ---- | ---- | -| **Build** | The Build tab provides options for making changes to your bot. This option is not available for **Registration Only Bot**. | -| **Test in Web Chat** | Use the integrated Web Chat control to help you quickly test your bot. | -| **Analytics** | If analytics is turned on for your bot, you can view the analytics data that Application Insights has collected for your bot. | -| **Channels** | Configure the channels your bot uses to communicate with users. | -| **Settings** | Manage various bot profile settings such as display name, analytics, and messaging endpoint. | -| **Speech priming** | Manage the connections between your LUIS app and the Bing Speech service.. | -| **Bot Service pricing** | Manage the pricing tier for the bot service. | +### Monitoring -## App service settings +In the **Monitoring** section are links for most of your bot's monitoring options. -![App Service Settings](~/media/azure-manage-a-bot/app-service-settings.png) +| Link | Description | +|--|--| +| **Conversational analytics** | Enables analytics to view the collected data with Application Insights. This Analytics blade will be deprecated. For more information, see [Add telemetry to your bot](/azure/bot-service/bot-builder-telemetry?view=azure-bot-service-4.0&tabs=csharp&WT.mc_id=Portal-Microsoft_Azure_BotService&preserve-view=true) and [Analyze your bot's telemetry data](/azure/bot-service/bot-builder-telemetry-analytics-queries?view=azure-bot-service-4.0&WT.mc_id=Portal-Microsoft_Azure_BotService&preserve-view=true). | +| **Alerts** | Configure alert rules and attend to fired alerts to efficiently monitor your Azure resources. For more information, see [Overview of alerts in Microsoft Azure](/azure/azure-monitor/alerts/alerts-overview?toc=%2Fazure%2Fazure-monitor%2Ftoc.json). | +| **Metrics** | Select a metric to see data in the proper chart. | +| **Diagnostic settings** | Diagnostic settings are used to configure streaming export of platform logs and metrics for a resource to the destination of your choice. For more information,see [diagnostic settings](/azure/azure-monitor/essentials/diagnostic-settings?WT.mc_id=Portal-Microsoft_Azure_Monitoring&tabs=CMD). | +| **Logs** | Produce insights from Azure Monitor logs. | -The **Application Settings** blade contains detailed information about your bot, such as the bot's environment, debug settings, and application settings keys. +## Application service settings -### MicrosoftAppID and MicrosoftAppPassword +A bot application, also known as an *application service (App Service)*, has a set of application settings that you can access through the Azure portal. They're environment variables passed to the bot application code. For more information, see [Configure an App Service app in the Azure portal](/azure/app-service/configure-common). -The **MicrosoftAppID** and **MicrosoftAppPassword** are kept within the bot's settings file(`appsettings.json` or `.env`), or Azure Key Vault. To retrieve them, either download your bot's setting or config file (for older bots, if it exists), or access Azure Key Vault. It may be necessary for you to test locally with the ID and password. +1. In your browser, navigate to the [Azure portal](https://ms.portal.azure.com/). +1. Search for your bot app service and select its name. +1. The bot app service information is displayed. -> [!NOTE] -> The **Bot Channels Registration** bot service comes with a *MicrosoftAppID* but because there is no app service associated with this type of service, there is no **Application Settings** blade for you to look up the *MicrosoftAppPassword*. To get the password, you must go generate one. To generate the password for a **Bot Channels Registration**, see [Bot Channels Registration password](bot-service-quickstart-registration.md#get-registration-password) +[!INCLUDE [azure bot appid password](includes/authentication/azure-bot-appid-password.md)] ## Next steps -Now that you have explored the Bot Service blade in the Azure portal, learn how to use the Online Code Editor to customize your bot. + +Now that you've explored the Bot Service blade in the Azure portal, learn about the Bot Framework Service, how bots communicate with users, and about activities, channels, HTTP POST requests, and more. + > [!div class="nextstepaction"] -> [Use the Online Code Editor](bot-service-build-online-code-editor.md) +> [How bots work](v4sdk/bot-builder-basics.md) diff --git a/articles/bot-service-manage-settings.md b/articles/bot-service-manage-settings.md index 62e25ee44..d06fa053f 100644 --- a/articles/bot-service-manage-settings.md +++ b/articles/bot-service-manage-settings.md @@ -1,37 +1,58 @@ --- title: Configure bot settings - Bot Service description: Learn how to configure the various options for your bot using the Azure portal. -keywords: configure bot settings, Display Name, Icon, Application Insights, Settings blade -author: v-royhar -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +keywords: configure bot settings, Display Name, Icon, Application Insights, Settings blade +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Configure bot settings -The bot settings, such as Display name, Icon, and Application Insights, can be viewed and modified on its **Settings** blade. +# Configure bot registration settings -![The bot settings blade](~/media/bot-service-portal-configure-settings/bot-settings-blade.png) +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -Below is the list of fields on the **Settings** blade: +The Azure Bot resource settings, such as display name, icon, and description, can be viewed and modified in the **Bot profile** pane. +The Azure Bot resource settings, such as messaging endpoint, Microsoft app ID, and Application Insights, can be viewed and modified in the **Configuration** pane. + +## Bot profile + +:::image type="content" source="media/bot-service-portal-configure-settings/bot-service-profile.png" alt-text="Bot profile settings."::: + +Below is the list of the **Bot profile** fields: | Field | Description | -| :--- | :--- | -| Icon | A custom icon to visually identify your bot in channels and as the icon for Skype, Cortana, and other services. This icon must be in PNG format, and no larger than 30K in size. This value can be changed at any time. | -| Display name | The name of your bot in channels and directories. This value can be changed at any time. 35 character limit. | -| Bot handle | Unique identifier for your bot. This value cannot be changed after creating your bot with the Bot Service. | +|:-|:-| +| Icon | A custom icon to visually identify your bot in channels and as the icon for your bot in Microsoft Teams or other services. | +| Display name | The name of your bot in channels and directories. You can change this value later. | +| Bot handle | A unique identifier for your bot. This value can't be changed after creating your bot with the Bot Service. | +| Description | A description of your bot. Some channels display the description. You can change this value later. | + +To save your changes, select **Apply** at the bottom of the blade. + +## Configuration + +:::image type="content" source="media/bot-service-portal-configure-settings/bot-service-settings.png" alt-text="Bot configuration settings."::: + +Below is the list of the **Configuration** fields: + +| Field | Description | +|:-|:-| | Messaging endpoint | The endpoint to communicate with your bot. | -| Microsoft App ID | Unique identifier for your bot. This value cannot be changed. You can generate a new password by clicking on the **Manage** link. | -| Application Insights Instrumentation key | Unique key for bot telemetry. Copy your Azure Application Insights Key to this field if you want to receive bot telemetry for this bot. This value is optional. Bots created in the Azure Portal will have this key generated for them. For more details on this field, see [Application Insights keys](~/bot-service-resources-app-insights-keys.md). | -| Application Insights API key | Unique key for bot analytics. Copy your Azure Application Insights API Key to this field if you want to view analytics about your bot in the Dashboard. This value is optional. For more details on this field, see [Application Insights keys](~/bot-service-resources-app-insights-keys.md). | -| Application Insights Application ID | Unique key for bot analytics. Copy your Azure Insights Application ID Key to this field if you want to view analytics about your bot in the Dashboard. This value is optional. Bots created in the Azure Portal will have this key generated for them. For more details on this field, see [Application Insights keys](~/bot-service-resources-app-insights-keys.md). | - -> [!NOTE] -> After you have changed settings for your bot, click the **Save** button at the top of the blade to save your new bot settings. - -## Next steps -Now that you have learned how to configure settings for your bot service, learn about how to configure speech priming. -> [!div class="nextstepaction"] -> [Speech priming](bot-service-manage-speech-priming.md) \ No newline at end of file +| Microsoft App ID | Unique identifier for your bot. This value can't be changed. You can generate a new password by clicking on the **Manage** link. | +| Schema Transformation Version | The bot schema transform version to use for this bot. For more information, see [Connect a bot to channels](bot-service-manage-channels.md). | +| Application Insights Instrumentation key | Unique key for bot telemetry. Copy your Azure Application Insights Key to this field if you want to receive bot telemetry for this bot. This value is optional. For more details on this field, see [Application Insights keys](bot-service-resources-app-insights-keys.md). | +| Application Insights API key | Unique key for bot analytics. Copy your Azure Application Insights API Key to this field if you want to view analytics about your bot in the Dashboard. This value is optional. For more details on this field, see [Application Insights keys](bot-service-resources-app-insights-keys.md). | +| Application Insights Application ID | Unique ID for bot analytics. Copy your Azure Insights Application ID Key to this field if you want to view analytics about your bot in the Dashboard. This value is optional. For more details on this field, see [Application Insights keys](bot-service-resources-app-insights-keys.md). | + +To save your changes, select **Apply** at the bottom of the blade. + +[!INCLUDE [app ID and password](includes/authentication/azure-bot-appid-password.md)] + +## Additional Information + +You can use [az bot update](/cli/azure/bot#az-bot-update) to update bot settings from the command line. diff --git a/articles/bot-service-manage-speech-priming.md b/articles/bot-service-manage-speech-priming.md deleted file mode 100644 index 375929f7f..000000000 --- a/articles/bot-service-manage-speech-priming.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Configure speech priming - Bot Service -description: Learn how to configure speech priming for your bot service using the Azure Portal. -keywords: speech priming, speech recognition, LUIS -author: v-royhar -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 ---- - -# Configure speech priming - -Speech priming improves the recognition of spoken words and phrases that are commonly used in your bot. For speech-enabled bots that use the Web Chat and Cortana channels, speech priming uses examples specified in Language Understanding (LUIS) apps to improve speech recognition accuracy for important words. - -Your bot may already be integrated with a LUIS app, or you can choose to create a LUIS app to associate with your bot for speech priming. The LUIS app contains examples of what you expect users to say to your bot. Important words that you want the bot to recognize should be labeled as entities. For example, in a chess bot you want to make sure that when the user says "Move knight", it isn’t interpreted as "Move night". The LUIS app should include examples in which "knight" is labeled as an entity. - -> [!NOTE] -> To use speech priming with the Web Chat channel, you must use the Bing Speech service. See [Enable speech in the Web Chat channel](~/bot-service-channel-connect-webchat-speech.md) for an explanation of how to use the Bing Speech service. - -> [!IMPORTANT] -> Speech priming only applies to bots configured for the Cortana channel or the Web Chat channel. - -> [!IMPORTANT] -> Priming is not supported from non U.S. regional LUIS apps including: eu.luis.ai and au.luis.ai - -## Change the list of LUIS apps your bot uses - -To change the list of LUIS apps used by Bing Speech with your bot, do the following: - -1. Click **Speech priming** on the bot service blade. The list of LUIS apps available to you will appear. -2. Select the LUIS apps you want Bing Speech to use. - - a. To select a LUIS app in the list, hover over the LUIS model until a checkbox appears, then check the checkbox. - - b. To select a LUIS app that is not on the list, scroll to the bottom and enter the LUIS Application ID GUID into the text box. - -3. Click **Save** to save the list of LUIS apps associated with Bing Speech for your bot. - -![The speech priming panel](~/media/bot-service-manage-speech-priming/speech-priming.png) - -## Additional Resources - -- To learn more about enabling speech in Web Chat see [Enable speech in the Web Chat channel](~/bot-service-channel-connect-webchat-speech.md). -- To learn more about speech priming, see [Speech Support in Bot Framework – Web Chat to Directline, to Cortana](https://blog.botframework.com/2017/06/26/Speech-To-Text/). -- To learn more about LUIS apps, see [Language Understanding Intelligent Service](https://www.luis.ai). diff --git a/articles/bot-service-migrate-bot.md b/articles/bot-service-migrate-bot.md deleted file mode 100644 index dec2842a4..000000000 --- a/articles/bot-service-migrate-bot.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Migrate your bot to Azure - Bot Service -description: Learn how to migrate your bot from the legacy Bot Framework Portal to a bot service in the Azure portal. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 3/22/2019 -monikerRange: 'azure-bot-service-3.0' ---- - -# Migrate your bot to Azure - -All **Azure Bot Service (Preview)** bots created in the [Bot Framework Portal](http://dev.botframework.com) must migrate to the new Bot Service in Azure. The service was made generally available (GA) in December 2017. - -Note that, registration bots connected only to the following channels are *not* required to migrate: **Teams**, **Skype**, or **Cortana**. For example, a registration bot connected to **Facebook** and **Skype** *is required* to migrate but a registration bot connected to **Skype** and **Cortana** *is not required* to migrate. - -> [!IMPORTANT] -> Before migrating a Functions bot created with Node.js, it is required that you use **Azure Functions Pack** to package the **node_modules** modules together. Doing so will improve performance during migration and execution of the Functions bot after it is migrated. -> To package your modules, see [Package a Functions bot with Funcpack](#package-a-functions-bot-with-funcpack). - -To migrate your bot, do the following: - -1. Sign into the [Bot Framework Portal](http://dev.botframework.com) and click **My bots**. -2. Click **Migrate** button for the bot you want to migrate. -3. Accept the **Terms** and click **Migrate** to start the migration process or click **Cancel** to cancel this action. - -> [!IMPORTANT] -> While the migration task is in progress, do not navigate away from the page or refresh the page. Doing so will cause the migration task to stop pre-maturely and you will have to perform the action again. -> To ensure the migration completes successfully, wait for the confirmation message. - -After the migration process completes successfully, the **Migration status** will indicate that the bot is migrated and a **Roll back migration** button will be available for a week after the migration date in case of issues. - -Clicking the name of a migrated bot will open the bot in the [Azure portal](https://portal.azure.com). - -## Package a Functions bot with Funcpack - -Functions bots created with Node.js must be packed using [Funcpack](https://github.com/Azure/azure-functions-pack) before migrating. To Funcpack your project, follow these steps: - -1. [Download](bot-service-build-download-source-code.md) your code locally if you haven’t already. -2. Update all the npm packages in **packages.json** to the latest versions and then run `npm install`. -3. Open **messages/index.js** and change `module.exports = { default: connector.listen() }` -to `module.exports = connector.listen();` -4. Install Funcpack via npm: `npm install -g azure-functions-pack` -5. To package the **node_modules** directory, run the following command: `funcpack pack ./` -6. Test your bot locally by running the Functions bot using the Bot Framework Emulator. More info on how to run the *funcpack* bot [here](https://github.com/Azure/azure-functions-pack#how-to-run). -7. Upload your code back to Azure. Make sure the `.funcpack` directory is uploaded. You do not need to upload the **node_modules** directory. -8. Test your remote bot to make sure it responds as expected. -9. Migrate your bot using the steps above. - -## Migration under the hood - -Depending on the type of bot you are migrating, the list below can help you better understand what is happening under the hood. - -* **Web App Bot** or **Functions Bot**: For these types of bots, the source code project is copied from the old bot to the new bot. Other resources such as your bot's storage, Application Insights, LUIS, etc. are left as is. In those cases, the new bot contains a copy of the IDs/keys/passwords to those existing resources. -* **Bot Channels Registration**: For this type of bots, the migration process simply create a new **Bot Channels Registration** and copy the endpoint from the old bot over. -* Regardless of the types of bots you are migrating, the migration process does not modify the existing bot's state in anyway. This allows you to safely roll back if you need to do so. -* If you have [continuous deployment](bot-service-build-continuous-deployment.md) set up, you will need to set it up again so that your source control connects to the new bot instead. - -## Understanding Azure Resources after migration -Once migration is done, your **Resource Group** will contain a number of Azure resources that are needed for your bot to work. The type and number of resources depend on the type of bot have you migrated. Refer to following sections to lean more. - -### Registration Bot - -This is the simplest type. The Resource Group in Azure will only contain one resource of type “Bot Channel Registration”. This is the equivalent of the previous bot record in the Bot Framework Developer Portal. - -![Bot Channel Registration bot listings in Azure](~/media/bot-service-migrate-bot/channel-registration-bot.png) - -### Web App Bot -The Web App bot migration will provision a Bot Service resource of type “Web App Bot” and a new App Service Web App (in green in the screenshot below). The previous Azure Bot Service (Preview) bot will still be there and can be deleted (in red in the screenshot below). - -![Web App bot listings in Azure](~/media/bot-service-migrate-bot/web-app-bot.png) - -### Functions Bot -The Functions bot migration will provision a Bot Service resource of type “Functions Bot” and a new App Service Functions App (in green in the screenshot below). The previous Azure Bot Service (Preview) bot will still be there and can be deleted (in red in the screenshot below). - -![Functions bot listings in Azure](~/media/bot-service-migrate-bot/functions-bot.png) - - -## Roll back migration - -In the event something went wrong with the bot during migration or after it is migrated, you can **Roll back migration**. To roll back a migration, do the following: - -1. Sign into the [Bot Framework Portal](http://dev.botframework.com) and click **My bots**. -2. Click **Roll back migration** button for the bot you want to roll back. A prompt will appear. -3. Click **Yes, roll back** to proceed or **Cancel** to cancel the roll back action. - -> [!NOTE] -> Roll back functionality will be available for a week after migration, and should be used only if you run into issues in the migrated bot. - -## Migration troubleshooting/Known issues -My node.js/functions bot migrated successfully, but it fails to respond: - -* **Check endpoint** - * Go to the **Settings** blade in your bot resource and verify that the bot endpoint has a “code” querystring parameter with a value in it. If not, you need to add it. -* **Check secrets folder in kudu for backups** - * In some rare cases there might be a few backup secret files that are creating a conflict. Go to the **home\data\Functions\secrets** folder in Kudu and delete any **host.snapshot** (or **host.backup**) file you find in there. There should be only one **host.json** and one **messages.json**. Finally restart the App Service and retry chatting with your bot. - -For any other issue please submit a CRI to Azure Support or file an issue in [GitHub](https://github.com/MicrosoftDocs/bot-framework-docs/issues). - - -## Next steps - -Now that your bot is migrated, learn how to manage your bot from the Azure portal. - -> [!div class="nextstepaction"] -> [Manage a bot](bot-service-manage-overview.md) diff --git a/articles/bot-service-overview-introduction.md b/articles/bot-service-overview-introduction.md deleted file mode 100644 index 83a7be227..000000000 --- a/articles/bot-service-overview-introduction.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: Azure Bot Service Introduction - Bot Service -description: Learn about Bot Service, a service for building, connecting, testing, deploying, monitoring, and managing bots. -keywords: overview, introduction, SDK, outline -author: Kaiqb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/15/2019 ---- - -# About Azure Bot Service - -[!INCLUDE [applies-to-both](includes/applies-to-both.md)] - -Azure Bot Service and Bot Framework provide tools to build, test, deploy, and manage intelligent bots, all in one place. Through the use of modular and extensible framework provided by the SDK, tools, templates, and AI services developers can create bots that use speech, understand natural language, handle questions and answers, and more. - -## What is a bot? -Bots provide an experience that feels less like using a computer and more like dealing with a person - or at least an intelligent robot. They can be used to shift simple, repetitive tasks, such as taking a dinner reservation or gathering profile information, on to automated systems that may no longer require direct human intervention. Users converse with a bot using text, interactive cards, and speech. A bot interaction can be a quick question and answer, or it can be a sophisticated conversation that intelligently provides access to services. - -Bots are a lot like modern web applications, living on the internet and use APIs to send and receive messages. What's in a bot varies widely depending on what kind of bot it is. Modern bot software relies on a stack of technology and tools to deliver increasingly complex experiences on a wide variety of platforms. However, a simple bot could just receive a message and echo it back to the user with very little code involved. - -Bots can do the same things other types of software can do - read and write files, use databases and APIs, and do the regular computational tasks. What makes bots unique is their use of mechanisms generally reserved for human-to-human communication. - -Azure Bot Service and Bot Framework offer: -- Bot Framework SDK for developing bots -- Bot Framework Tools to cover end-to-end bot development workflow -- Bot Framework Service (BFS) to send and receive messages and events between bots and channels -- Bot deployment and channel configuration in Azure - -Additionally, bots may use other Azure services, such as: -- Azure Cognitive Services to build intelligent applications -- Azure Storage for cloud storage solution - -## Building a bot - -Azure Bot Service and Bot Framework offer an integrated set of tools and services to facilitate this process. Choose your favorite development environment or command line tools to create your bot. SDKs exist for C#, JavaScript, and Typescript. (SDKs for Java and Python are under development.) We provide tools for various stages of bot development to help you design and build bots. - -![Bot Overview](media/bot-service-overview.png) - -### Plan -As with any type of software, having a thorough understanding of the goals, processes and user needs is important to the process of creating a successful bot. Before writing code, review the bot [design guidelines](bot-service-design-principles.md) for best practices and identify the needs for your bot. You can create a simple bot or include more sophisticated capabilities such as speech, natural language understanding, and question answering. - -### Build -Your bot is a web service that implements a conversational interface and communicates with the Bot Framework Service to send and receive messages and events. Bot Framework Service is one of the components of the Azure Bot Service and Bot Framework. You can create bots in any number of environments and languages. You can start your bot development in the [Azure portal](bot-service-quickstart.md), or use [[C#](dotnet/bot-builder-dotnet-sdk-quickstart.md) | [JavaScript](javascript/bot-builder-javascript-quickstart.md) | [Python](python/bot-builder-python-quickstart.md)] templates for local development. - -As part of the Azure Bot Service and Bot Framework, we offer additional components you can use to extend your bot's functionality: - -| Feature | Description | Link | -| --- | --- | --- | -| Add natural language processing | Enable your bot to understand natural language, understand spelling errors, use speech, and recognize the user's intent | How to use [LUIS](~/v4sdk/bot-builder-howto-v4-luis.md) -| Answer questions | Add a knowledge base to answer questions users ask in a more natural, conversational way | How to use [QnA Maker](~/v4sdk/bot-builder-howto-qna.md) -| Manage multiple models | If using more than one model, such as for LUIS and QnA Maker, intelligently determine when to use which one during your bot's conversation | [Dispatch](~/v4sdk/bot-builder-tutorial-dispatch.md) tool| -| Add cards and buttons | Enhance the user experience with media other than text, such as graphics, menus, and cards | How to [add cards](v4sdk/bot-builder-howto-add-media-attachments.md) | - -> [!NOTE] -> The table above is not a comprehensive list. Explore the articles on the left, starting with [sending messages](~/v4sdk/bot-builder-howto-send-messages.md), for more bot functionality. - -Additionally, we provide command line tools to help you to create, manage, and test bot assets. These tools can configure LUIS apps, build a QnA knowledge base, build models to dispatch between components, mock a conversation, and more. You can find more details in the command line tools [readme](https://aka.ms/botbuilder-tools-readme). - -You also have access to a variety of [samples](https://github.com/microsoft/botbuilder-samples) that showcase many of the capabilities available through the SDK. These are great for developers looking for a more feature rich starting point. - -### Test -Bots are complex apps, with a lot of different parts working together. Like any other complex app, this can lead to some interesting bugs or cause your bot to behave differently than expected. Before publishing, test your bot. We provide several ways to test bots before they are released for use: - -- Test your bot locally with the [emulator](bot-service-debug-emulator.md). The Bot Framework Emulator is a stand-alone app that not only provides a chat interface but also debugging and interrogation tools to help understand how and why your bot does what it does. The emulator can be run locally alongside your in-development bot application. - -- Test your bot on the [web](bot-service-manage-test-webchat.md). Once configured through the Azure portal your bot can also be reached through a web chat interface. The web chat interface is a great way to grant access to your bot to testers and other people who do not have direct access to the bot's running code. - -- [Unit Test](https://docs.microsoft.com/azure/bot-service/unit-test-bots) your bot with the July update of Bot Framework SDK. - - - -### Publish -When you are ready for your bot to be available on the web, publish your bot to [Azure](bot-builder-howto-deploy-azure.md) or to your own web service or data center. Having an address on the public internet is the first step to your bot coming to life on your site, or inside chat channels. - -### Connect          -Connect your bot to channels such as Facebook, Messenger, Kik, Skype, Slack, Microsoft Teams, Telegram, text/SMS, Twilio, Cortana, and Skype. Bot Framework does most of the work necessary to send and receive messages from all of these different platforms - your bot application receives a unified, normalized stream of messages regardless of the number and type of channels it is connected to. For information on adding channels, see [channels](bot-service-manage-channels.md) topic. - -### Evaluate -Use the data collected in Azure portal to identify opportunities to improve the capabilities and performance of your bot. You can get service-level and instrumentation data like traffic, latency, and integrations. Analytics also provides conversation-level reporting on user, message, and channel data. For more information, see [how to gather analytics](bot-service-manage-analytics.md). - - -## Next steps -Check out these [case studies](https://azure.microsoft.com/services/bot-service/) of bots or click on the link below to create a bot. -> [!div class="nextstepaction"] -> [Create a bot](bot-service-quickstart.md) diff --git a/articles/bot-service-overview-readme.md b/articles/bot-service-overview-readme.md deleted file mode 100644 index f1d45c306..000000000 --- a/articles/bot-service-overview-readme.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: How Bot Service works - Bot Service -description: Learn about the features and capabilities of Bot Service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# How Bot Service works - -Bot Service provides the core components for creating bots, including the Bot Framework SDK for developing bots and the Bot Framework for connecting bots to channels. Bot Service provides five templates you can choose from when creating your bots with support for .NET and Node.js. - -> [!IMPORTANT] -> You must have a Microsoft Azure subscription before you can use Bot Service. If you do not already have a subscription, you can register for a free account. - -## Hosting plans -Bot Service provides two hosting plans for your bots. You can choose a hosting plan that suits your needs. - -### App Service plan - -A bot that uses an App Service plan is a standard Azure web app you can set to allocate a predefined capacity with predictable costs and scaling. With a bot that uses this hosting plan, you can: - -* Edit bot source code online using an advanced in-browser code editor. -* Download, debug, and re-publish your C# bot using Visual Studio. -* Set up continuous deployment easily for Visual Studio Online and Github. -* Use sample code prepared for the Bot Framework SDK. - -### Consumption plan -A bot that uses a Consumption plan is a serverless bot that runs on Azure Functions, and uses the pay-per-run Azure Functions pricing. A bot that uses this hosting plan can scale to handle huge traffic spikes. You can edit bot source code online using a basic in-browser code editor. For more information about the runtime environment of a Consumption plan bot, see Azure Functions Consumption and App Service plans. - -## Templates - -Bot Service enables you to quickly and easily create a bot in either C# or Node.js by using one of five templates. - -[!INCLUDE [Bot Service templates](~/includes/snippet-abs-templates.md)] - -[Learn more](bot-service-concept-templates.md) about the different templates and how they can help you build bots. - -## Develop and deploy - -By default, Bot Service enables you to develop your bot directly in the browser using the Online Code Editor, without any need for a tool chain. - -You can develop and debug your bot locally with the Bot Framework SDK and an IDE, such as Visual Studio 2017. You can publish your bot directly to Azure using Visual Studio 2017 or the Azure CLI. You can also [set up continuous deployment](bot-service-continuous-deployment.md) with the source control system of your choice, such as VSTS or GitHub. With continuous deployment configured, you can develop and debug in an IDE on your local computer, and any code changes that you commit to source control automatically deploy to Azure. - -> [!TIP] -> After enabling continuous deployment, be sure to modify your code through continuous deployment only and not through other mechanisms to avoid conflict. - -## Manage your bot - -During the process of creating a bot using Bot Service, you specify a name for your bot, set up its hosting plan, choose a pricing tier, and configure some other settings. After your bot has been created, you can change its settings, configure it to run on one or more channels, and test it with in Web Chat. - -## Next steps - -> [!div class="nextstepaction"] -> [Create a bot with Bot Service](bot-service-quickstart.md) \ No newline at end of file diff --git a/articles/bot-service-overview.md b/articles/bot-service-overview.md new file mode 100644 index 000000000..86cb428d5 --- /dev/null +++ b/articles/bot-service-overview.md @@ -0,0 +1,123 @@ +--- +title: What is the Bot Framework SDK? +description: The Bot Framework, along with the Azure AI Bot Service, provides tools to build, test, deploy, and manage intelligent bots, all in one place. The Bot Framework includes a modular and extensible SDK for building bots, as well as tools, templates, and related AI services. With this framework, developers can create bots that use speech, understand natural language, handle questions and answers, and more. +keywords: overview, introduction, SDK, outline +displayName: About the Bot Framework SDK, About the Azure AI Bot Service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: overview +ms.custom: + - abs-meta-21q1 + - evergreen +--- + +# What is the Bot Framework SDK? + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Microsoft Bot Framework and Azure AI Bot Service are a collection of libraries, tools, and services that let you build, test, deploy, and manage intelligent bots. The Bot Framework includes a modular and extensible SDK for building bots and connecting to AI services. With this framework, developers can create bots that use speech, understand natural language, answer questions, and more. + +> [!TIP] +> For a list of Microsoft products and services for building bots, who they support, and a brief description of each, see [Choose the right chatbot solution for your use case](/azure/bot-service/bot-overview). + +## What is a bot? + +Bots provide an experience that feels less like using a computer and more like dealing with a person—or intelligent robot. You can use bots to shift simple, repetitive tasks—such as taking a dinner reservation or gathering profile information—onto automated systems that may no longer require direct human intervention. +Users converse with a bot using text, interactive cards, and speech. +A bot interaction can be a quick answer to a question or an involved conversation that intelligently provides access to services. + +One way to think of a bot is as a web application that has a conversational interface. +Your users connect to your bot through a channel, such as Facebook, Slack, Microsoft Teams, or a custom application. + +- Depending on how the bot is configured and how it's registered with the channel, interactions can be in text or speech and can include images and video. +- The bot processes the user's input to interpret what the user has asked for or said. +- The bot evaluates input and performs relevant tasks, such as ask the user for additional information or access services on behalf of the user. +- The bot responds to the user to let them know what the bot is doing or has done. + +:::image type="content" source="media/architecture/what-is-a-bot.png" alt-text="A remote bot interacts with a user on a device via text, speech, images, or video."::: + +Bots are often implemented as a web application, hosted in Azure and using APIs to send and receive messages. +What's in a bot varies widely depending on what kind of bot it is and what its purpose is. +A bot can receive messages and echo them back to the user, with little code involved. +A more complex bot can rely on various tools and services to deliver richer experiences on a wide variety of platforms. + +Bots can do the same things other types of software can do—read from and write to files, use databases and APIs, and do the regular computational tasks. +What makes bots unique is their use of mechanisms used in human-to-human communication. + +Azure AI Bot Service and the Bot Framework include: + +- Bot Framework SDKs for developing bots in C#, JavaScript, Python, or Java. + (The Java SDK is retired with final long-term support ending in November 2023.) +- CLI tools for help with end-to-end bot development. +- Bot Connector Service, which relays messages and events between bots and channels. +- Azure resources for bot management and configuration. + +Additionally, bots may use other Azure services, such as: + +- Azure AI services to build intelligent applications +- Azure Storage for cloud storage solution + +## How to build a bot + +Azure AI Bot Service and Microsoft Bot Framework offer an integrated set of tools and services to help you design and build bots, through all stages of the bot life cycle. +SDKs exist for C#, Java, JavaScript, TypeScript, and Python. +Choose your favorite development environment or command line tools to create your bot. + +:::image type="content" source="media/bot-service-overview.png" alt-text="Illustration of the steps in the bot life cycle."::: + +### Plan + +As with any type of software, having a thorough understanding of the goals, processes and user needs is important to the process of creating a successful bot. +You can create a simple bot or include more sophisticated capabilities such as speech, natural language understanding, and question answering. + +Before writing code, review the bot [design guidelines](bot-service-design-principles.md) for best practices and to identify the needs for your bot. + +### Build + +Typically, a bot is a web service hosted in Azure. +In Azure, you can configure your bot to send and receive messages and events from various channels. +You can create bots in any number of environments and languages. You can [create a bot](bot-service-quickstart-create-bot.md) for local development. + +With Azure AI Bot Service and the Bot Framework, you can use other libraries and services to extend your bot's functionality. This table describes some of the features supported by the SDK. + +| Feature | Description | More information | +|:-|:-|:-| +| Memory and storage | Persist user and conversation state | [Managing state](v4sdk/bot-builder-concept-state.md) | +| Natural language understanding | Interpret and extract information from user input | [Language understanding](v4sdk/bot-builder-concept-luis.md) | +| Rich cards | Combine text and other media, such as images, audio, video, and buttons | [How to add media and cards](v4sdk/bot-builder-howto-add-media-attachments.md) | + +Command line tools to help you to create, manage, and test bot assets. +For more information, see [Azure CLI](/cli/azure/) and [Bot Framework Tools](https://github.com/microsoft/botframework-cli#readme). + +For complete code samples, see the [Bot Framework Samples repo](https://github.com/microsoft/botbuilder-samples#readme). +The samples demonstrate many capabilities of the SDK. + +### Test + +Bots are complex apps with many different parts working together. Like any other complex app, this can lead to some interesting bugs or cause your bot to behave differently than expected. Before publishing, test your bot. We provide several ways to test bots before they're released for use: + +- Test your bot locally with the [Bot Framework Emulator](bot-service-debug-emulator.md). The Bot Framework Emulator is a stand-alone app that not only provides a chat interface but also debugging and interrogation tools to help understand how and why your bot does what it does. The Emulator can be run locally alongside your in-development bot application. + +- Test your bot on the [web](bot-service-manage-test-webchat.md). Once configured through the Azure portal your bot can also be reached through a web chat interface. The web chat interface is a great way to grant access to your bot to testers and other people who don't have direct access to the bot's running code. + +- [Unit Test](/azure/bot-service/unit-test-bots) your bot with the current Bot Framework SDK. + +### Publish + +When you're ready for your bot to be available on the web, [deploy your bot to Azure](provision-and-publish-a-bot.md) or deploy to your own web service or data center. Having an address on the public internet is the first step to your bot coming to life on your site, or inside chat channels. + +### Connect + +Connect your bot to channels, such as Facebook, Messenger, Slack, Microsoft Teams, Telegram, and SMS via Twilio. Bot Framework does most of the work necessary to send and receive messages from all of these different platforms—your bot application receives a unified, normalized stream of messages regardless of the number and type of channels it's connected to. For information on adding channels, see [channels](bot-service-manage-channels.md) topic. + +### Evaluate + +Use the data collected in Azure portal to identify opportunities to improve the capabilities and performance of your bot. You can get service-level and instrumentation data like traffic, latency, and integrations. Analytics also provides conversation-level reporting on user, message, and channel data. For more information, see [how to gather analytics](bot-service-manage-analytics.md). + +## Next steps + +- [Read customer stories](https://azure.microsoft.com/services/bot-services/#customer-stories) +- [Create a Bot Framework bot](bot-service-quickstart.md) diff --git a/articles/bot-service-quickstart-create-bot.md b/articles/bot-service-quickstart-create-bot.md new file mode 100644 index 000000000..3c1691ad2 --- /dev/null +++ b/articles/bot-service-quickstart-create-bot.md @@ -0,0 +1,365 @@ +--- +title: Create a basic bot +description: Create your first bot with the Bot Framework SDK, in C#, Java, JavaScript, or Python. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: quickstart +ms.custom: + - mode-api + - tab-zone-seo + - abs-meta-21q1 + - evergreen +--- + +# Create a bot with the Bot Framework SDK + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +This article describes how to build your first bot with the Bot Framework SDK for C#, Java, JavaScript or Python, and how to test your bot with the Bot Framework Emulator. + +Creating your first bot doesn't require an Azure subscription or an Azure AI Bot Service resource. This quickstart focuses on creating your first bot locally. If you'd like to learn how to create a bot in Azure, see [Create an Azure Bot resource](./v4sdk/abs-quickstart.md). + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] + +## Prerequisites + +### [C#](#tab/csharp) + +- [.NET 6.0 SDK](https://dotnet.microsoft.com/download) +- [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) +- Knowledge of [ASP.NET Core](/aspnet/core/) and [asynchronous programming in C#](/dotnet/csharp/programming-guide/concepts/async/index) + +### C# templates + +The current bot samples use .NET Core 3.1 templates. + +[!INCLUDE [Add templates in C#](includes/quickstart/dotnet/add-templates.md)] + +### [Java](#tab/java) + +- Java 1.8 or later +- [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) +- [Visual Studio Code](https://www.visualstudio.com/downloads) or your favorite IDE, if you want to edit the bot code. +- Install [Maven](https://maven.apache.org/) +- Install [node.js](https://nodejs.org/) version 12.10 or later. +- An Azure account if you want to deploy to [Azure](https://azure.microsoft.com/). + +### Java templates + +Use the Yeoman generator to quickly create a conversational AI bot using core AI capabilities in the [Bot Framework v4](https://dev.botframework.com). For more information, see [yeoman.io](https://yeoman.io). + +The generator supports three different template options as shown below. + +| Template | Description | +| ---------- | --------- | +| Echo Bot | A good template if you want a little more than "Hello World!", but not much more. This template handles the very basics of sending messages to a bot, and having the bot process the messages by repeating them back to the user. This template produces a bot that simply "echoes" back to the user anything the user says to the bot. | +| Empty Bot | A good template if you're familiar with Bot Framework v4, and simply want a basic skeleton project. Also a good option if you want to take sample code from the documentation and paste it into a minimal bot in order to learn. | +| Core Bot | A good template if you want to create advanced bots, as it uses multi-turn dialogs and [Azure AI LUIS](https://www.luis.ai) to implement language understanding. This template creates a bot that can extract places and dates to book a flight. | + +[!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] + +### Install Yeoman + +1. Assure that you have installed [node.js](https://nodejs.org/) version 12.10 or later. +1. Install latest [npm](https://www.npmjs.com). + + ```console + npm install -g npm + ``` + +1. Install [Yeoman](http://yeoman.io). Make sure to install globally. + + ```console + npm install -g yo + ``` + +1. Install *generator-botbuilder-java*. Make sure to install globally. + + ```console + npm install -g generator-botbuilder-java + ``` + +1. Verify that *Yeoman* and *generator-botbuilder-java* have been installed correctly. + + ```console + yo botbuilder-java --help + ``` + +### [JavaScript](#tab/javascript) + +- [Node.js](https://nodejs.org/) +- [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) +- Knowledge of [restify](http://restify.com/) and asynchronous programming in JavaScript +- [Visual Studio Code](https://www.visualstudio.com/downloads) or your favorite IDE, if you want to edit the bot code. + +### JavaScript and TypeScript templates + +To install Yeoman and the Yeoman generator for Bot Framework v4: + +1. Open a terminal or elevated command prompt. + +1. Switch to the directory for your JavaScript bots. Create it first if you don't already have one. + + ```console + mkdir myJsBots + cd myJsBots + ``` + +1. Make sure you have the latest versions of npm and Yeoman. + + ```console + npm install -g npm + npm install -g yo + ``` + +1. Install the Yeoman generator. +Yeoman is a tool for creating applications. For more information, see [yeoman.io](https://yeoman.io). + + ```console + npm install -g generator-botbuilder + ``` + + > [!NOTE] + > The install of Windows build tools listed below is only required if you use Windows as your development operating system. + > For some installations, the install step for restify is giving an error related to `node-gyp`. + > If this is the case you can try running this command with elevated permissions. + > This call may also hang without exiting if Python is already installed on your system: + > + > Only run this command if you're on Windows. + > + > ```console + > npm install -g windows-build-tools + > ``` + +### [Python](#tab/python) + +- Python [3.8.3][] +- [Bot Framework Emulator][Emulator] +- Knowledge of asynchronous programming in Python + +### Create and enable a virtual environment + +A virtual environment is a combination of a specific Python interpreter and libraries that are different from your global settings. The virtual environment is specific to a project and is maintained in the project folder. A benefit to using a virtual environment is that as you develop a project over time, the virtual environment always reflects the project's exact dependencies. To learn more about virtual environments, see [Creation of virtual environments](https://docs.python.org/3/library/venv.html). + +Navigate to the directory where you want to create your bot. Then run the following commands for your preferred platform. After you activate your virtual environment, your command line/terminal should be prefaced with `(venv)`. This lets you know that the virtual environment is active. You can deactivate your virtual environment at any time by typing: `deactivate`. + +**macOS/Linux** + +```bash +python3 -m venv venv +source venv/bin/activate +``` + +**Windows** + +```console +python -m venv venv +venv\Scripts\activate.bat +``` + +### Python templates + +Install the necessary packages by running the following `pip install` commands: + +```console +pip install botbuilder-core +pip install asyncio +pip install aiohttp +pip install cookiecutter==1.7.0 +``` + +>[!IMPORTANT] +>If you're using a 32-bit version of Python, make sure you also run `pip install cryptography==2.8`. + +[3.8.3]: https://www.python.org/downloads/release/python-383/ +[Emulator]: https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md + +--- + +## Create a bot + +### [C#](#tab/csharp) + +[!INCLUDE [Create a bot in C#](includes/quickstart/dotnet/create-bot.md)] + +> [!div class="nextstepaction"] +> [I created an echo bot](#start-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=create-a-bot&PLanguage=Csharp) + +### [Java](#tab/java) + +Run the following command to create an echo bot from templates. The command uses default options for its parameters. + +```console +yo botbuilder-java -T "echo" +``` + +Yeoman prompts you for some information with which to create your bot. For this tutorial, use the default values. + +```text +? What's the name of your bot? (echo) +? What's the fully qualified package name of your bot? (com.mycompany.echo) +? Which template would you like to start with? (Use arrow keys) Select "Echo Bot" +? Looking good. Shall I go ahead and create your new bot? (Y/n) Enter "y" +``` + +The generator supports many command-line options you can use to change the generator's defaults or to pre-seed a prompt. The options are case-sensitive. + +| Command-line option | Description | +|--|--| +| `--help, -h` | List help text for all supported command-line options | +| `--botName, -N` | The name given to the bot project | +| `--packageName, -P` | The Java package name to use for the bot | +| `--template, -T` | The template used to generate the project. Options are `echo`, `empty`, `core`. For more information about the different templates, see the GitHub repository for your language, [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates#readme), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder#readme), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators#readme), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators#readme). | +| `--noprompt` | The generator won't prompt for confirmation before creating a new bot. Any requirement options not passed on the command line will use a reasonable default value. This option is intended to enable automated bot generation for testing purposes. | + +Thanks to the template, your project contains all the code that's necessary to create the bot in this quickstart. You don't need any other code to test your bot. + +> [!div class="nextstepaction"] +> [I created an echo bot](#start-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=create-a-bot&PLanguage=Java) + +### [JavaScript](#tab/javascript) + +1. Use the generator to create an echo bot. + + ```console + yo botbuilder + ``` + + Yeoman prompts you for some information with which to create your bot. For this tutorial, use the default values. + + ```text + ? What's the name of your bot? my-chat-bot + ? What will your bot do? Demonstrate the core capabilities of the Microsoft Bot Framework + ? What programming language do you want to use? JavaScript + ? Which template would you like to start with? Echo Bot - https://aka.ms/bot-template-echo + ? Looking good. Shall I go ahead and create your new bot? Yes + ``` + +Thanks to the template, your project contains all the code that's necessary to create the bot in this quickstart. You don't need any other code to test your bot. + +> [!div class="nextstepaction"] +> [I created an echo bot](#start-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=create-a-bot&PLanguage=Javascript) + +### [Python](#tab/python) + +1. From your working directory, run the following command to download the [echo bot][echo-template] template and its dependencies: + + ```console + cookiecutter https://github.com/microsoft/BotBuilder-Samples/releases/download/Templates/echo.zip + ``` + +1. You'll be prompted to give your bot a name and description. Enter the following values: + - **bot_name**: *echo_bot* + - **bot_description**: *A bot that echoes back user response.* + + :::image type="content" source="media/python/quickstart/set-name-description.png" alt-text="Set Python bot name and description"::: + +> [!div class="nextstepaction"] +> [I created an echo bot](#start-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=create-a-bot&PLanguage=Python) + +[echo-template]: https://github.com/microsoft/BotBuilder-Samples/blob/main/samples/csharp_dotnetcore/01.console-echo/EchoBot.cs + +--- + +## Start your bot + +### [C#](#tab/csharp) + +[!INCLUDE [Start your bot in C#](includes/quickstart/dotnet/start-bot.md)] + +> [!div class="nextstepaction"] +> [I started the echo bot](#start-the-emulator-and-connect-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=start-your-bot&PLanguage=Csharp) + +### [Java](#tab/java) + +1. From a terminal, navigate to the directory where you saved your bot, then execute the commands listed below. + +1. Build the Maven project and packages it into a *.jar* file (archive). + + ```console + mvn package + ``` + +1. Run the bot locally. Replace the *archive-name* with the actual name from the previous command. + + ```console + java -jar .\target\.jar + ``` + +You're now ready to start the Emulator. + +> [!div class="nextstepaction"] +> [I started the echo bot](#start-the-emulator-and-connect-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=start-your-bot&PLanguage=Java) + +### [JavaScript](#tab/javascript) + +In a terminal or command prompt change directories to the one created for your bot, and start it with `npm start`. + +```console +cd my-chat-bot +npm start +``` + +At this point, your bot is running locally on port 3978. + +> [!div class="nextstepaction"] +> [I started the echo bot](#start-the-emulator-and-connect-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=start-your-bot&PLanguage=Javascript) + +### [Python](#tab/python) + +1. From the command line/terminal, change directories to `echo_bot`. + + ```console + cd echo_bot + ``` + +1. Install the dependencies for the echo bot template. + + ```console + pip install -r requirements.txt + ``` + +1. After the dependencies are installed, run the following command to start your bot: + + ```console + python app.py + ``` + + You'll know your bot is ready to test when you see the last line shown in the screenshot below: + + :::image type="content" source="media/python/quickstart/bot-running-locally.png" alt-text="Python bot running locally"::: + +1. Copy the http address in the last line. You'll need it when you use the Emulator to interact with your bot. + +> [!div class="nextstepaction"] +> [I started the echo bot](#start-the-emulator-and-connect-your-bot) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=start-your-bot&PLanguage=Python) + +--- + +## Start the Emulator and connect your bot + +1. Start the Bot Framework Emulator. +1. Select **Open Bot** on the Emulator's **Welcome** tab. +1. Enter your bot's URL, which is your local host and port, with `/api/messages` added to the path. The address is usually: `http://localhost:3978/api/messages`. + + :::image type="content" source="media/quickstart/emulator-open-bot.png" alt-text="open a bot"::: + +1. Then select **Connect**. + + Send a message to your bot, and the bot will respond back. + + :::image type="content" source="media/quickstart/emulator-hello-echo.png" alt-text="echo message"::: + +> [!div class="nextstepaction"] +> [I started the Emulator and connected to my bot](#next-steps) [I ran into an issue](https://microsoft.qualtrics.com/jfe/form/SV_6D4KLPZc2jTIa2O?Product=BotSDK&Page=bot-service-quickstart-create-bot&Section=start-the-emulator-and-connect-your-bot) + +## Next steps + +- For information about how to debug using Visual Studio or Visual Studio Code and the Bot Framework Emulator, see [Debug a bot](bot-service-debug-channel-devtunnel.md). +- For information about devtunnel, see [Tunneling (devtunnel)](https://github.com/Microsoft/BotFramework-Emulator/wiki/Tunneling-(devtunnel)). + +> [!div class="nextstepaction"] +> [Deploy your bot to Azure](bot-builder-deploy-az-cli.md) diff --git a/articles/bot-service-quickstart-registration.md b/articles/bot-service-quickstart-registration.md index 6bc4a49f2..bce5896ce 100644 --- a/articles/bot-service-quickstart-registration.md +++ b/articles/bot-service-quickstart-registration.md @@ -1,108 +1,61 @@ --- -title: Create a Bot Channels Registration with Bot Service - Bot Service -description: Learn how to register an existing bot with Bot Service. -ms.author: kamrani -manager: kamrani -ms.topic: conceptual -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' +title: Register a Bot Framework bot with Azure +description: If you don't currently host your bot in Azure, you can still make it available in Azure. To do so, you enter in Azure the web address where your bot is hosted. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen +monikerRange: 'azure-bot-service-4.0' --- -# Register a bot with Azure Bot Service +# Register a bot with Azure -This topic shows you how to create an **Azure Bot Service** resource to register your bot. You need this if the bot is hosted elsewhere and you want to make it available in Azure and connect it to Azure Bot Service channels. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -This allows you to build, connect, and manage your bot to interact with users, wherever they are, via Cortana, Skype, Messenger and many other services. +If you don't currently host your bot in Azure, you can still make it available in Azure and use Azure to connect your bot to channels. To do so, enter in Azure the web address where your bot is hosted. -> [!IMPORTANT] -> You only need to register your bot if it is not hosted in Azure. If you [created a bot](v4sdk/abs-quickstart.md) through the Azure portal then it is already registered with the service. +This article shows how to register such a bot with Azure AI Bot Service. -## Create a registration resource - -1. In your browser, navigate to the [Azure portal](https://ms.portal.azure.com). - - > [!TIP] - > If you do not have a subscription, you can register for a free account. - -1. In the left pane, click **Create a resource**. -1. In the right pane selection box enter *bot*. And in the drop-down list, select **Bot Channels Registration** or **Web App Bot** depending on your application. -For the **Web Bot App**, follow the steps described in the article: [Create a bot with Azure Bot Service](v4sdk/abs-quickstart.md). You will create a bot in Azure that is automatically registered with the Azure Bot Service. -1. Click the **Create** button to start the process. -1. In the **Bot Service** blade, provide the requested information about your bot as specified in the table below the image. - - ![Create registration bot blade](media/azure-bot-quickstarts/registration-create-bot-service-blade.png) - - |Setting |Suggested value|Description| - |---|---|--| - |**Bot name** |Your bot's display name|The display name for the bot that appears in channels and directories. This name can be changed at anytime.| - |**Subscription**|Your subscription|Select the Azure subscription you want to use.| - |**Resource Group**|myResourceGroup|You can create a new [resource group](/azure/azure-resource-manager/resource-group-overview#resource-groups) or choose from an existing one.| - |**Location**|West US|Choose a location near where your bot is deployed or near other services your bot will access.| - |**Pricing tier**|F0|Select a pricing tier. You may update the pricing tier at any time. For more information, see [Bot Service pricing](https://azure.microsoft.com/pricing/details/bot-service/).| - |**Messaging endpoint**|URL|Enter the URL for your bot's messaging endpoint.| - |**Application Insights**|On| Decide if you want to turn [Application Insights](bot-service-manage-analytics.md) **On** or **Off**. If you select **On**, you must also specify a regional location. | - |**Microsoft App ID and password**| Auto create App ID and password |Use this option if you need to manually enter a Microsoft App ID and password. See the next section [Manual app registration](#manual-app-registration). Otherwise, a new Microsoft App ID and password will be created in registration process. | - - > [!IMPORTANT] - > Do not forget to enter the URL for your bot's messaging endpoint. - -1. Click the **Create** button. Wait for the resource to be created. It will show in your resources list. - -### Get registration password - -After the registration is completed, Azure Active Directory assigns a unique application ID to your registration, and you're taken to your application's *Overview* page. - -To obtain the password follow the steps described next. - -1. In resource list, click on the Azure App Service resource just created. -1. Im the right pane, in the resource blade, click **Settings**. The resource *Settings* page is displayed. -1. In the Settings page copy the generated **Microsoft App ID** and save it to a file. -1. Click the **Manage** link by *Microsoft App ID*. - - ![Create registration bot blade](media/azure-bot-quickstarts/bot-channels-registration-app-settings.png) - -1. In the *Certificates & secrets* displayed page, click the **New client secret** button. +> [!IMPORTANT] +> You only need to register a bot if it's not hosted in Azure. +> Bots created using the Azure CLI are already registered with the Azure AI Bot Service. - ![Create registration bot blade](media/azure-bot-quickstarts/bot-channels-registration-app-secrets.png) +[!INCLUDE [identity-app-type-support](./includes/azure-bot-resource/identity-app-type-support.md)] -1. Add the description, select the expiration time, and click the **Add** button. +This article doesn't describe how to create or deploy the bot to register. For more information, see: - ![Create registration bot blade](media/azure-bot-quickstarts/bot-channels-registration-app-secrets-create.png) +- The [Create a bot](bot-service-quickstart-create-bot.md) quickstart +- The [Deploy a basic bot](bot-builder-deploy-az-cli.md) tutorial - This will generate a new password for your bot. Copy this password and save it to a file. This is the only time you will see this password. If you do not have the full password saved, you will need to repeat the process to create a new password should you need it later. +[!INCLUDE [Azure bot resource](includes/azure-bot-resource/azure-bot-resource.md)] ## Manual app registration -A manual registration is necessary for situations like: - -- You are unable to make the registrations in your organization and need another party to create the App ID for the bot you're building. -- You need to manually create your own app ID (and password). +A manual registration is necessary when: -See [FAQ - App Registration](bot-service-resources-bot-framework-faq.md#app-registration). - -> [!IMPORTANT] -> In the section *Supports account types*, you must choose one of the 2 multi-tenant types that is: *Accounts in any organizational directory (Any Azure AD - Multitenant)* or *Accounts in any organizational directory (Any Azure AD - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)*, when creating the app, otherwise the bot will not work. For more information, see [Register a new application using the Azure portal](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app#register-a-new-application-using-the-azure-portal). +- You're unable to make the registrations in your organization and need another party to create the App ID for the bot you're building. +- You need to manually create your own app ID and password. ## Update the bot -If you're using the Bot Framework SDK for .NET, set the following key values in the web.config file: - -- `MicrosoftAppId = ` -- `MicrosoftAppPassword = ` +To update your bot's configuration file to include its app ID and password, see [Application ID and password](bot-service-manage-settings.md#bot-identity-information) in how to **Configure bot registration settings**. -If you're using the Bot Framework SDK for Node.js, set the following environment variables: +## Additional information -- `MICROSOFT_APP_ID = ` -- `MICROSOFT_APP_PASSWORD = ` +See these articles for more information about Azure applications in general. -## Test the bot - -Now that your bot service is created, [test it in Web Chat](bot-service-manage-test-webchat.md). Enter a message and your bot should respond. +| Subject | Article | +|:-|:-| +| App registration | [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app) | +| Managed identities | [What are managed identities for Azure resources?](/azure/active-directory/managed-identities-azure-resources/overview) | +| Single-tenant and multi-tenant apps | [Tenancy in Microsoft Entra ID](/azure/active-directory/develop/single-and-multi-tenant-apps) | ## Next steps -In this topic, you learned how to register your hosted bot with the Bot Service. The next step is to learn how to manage your Bot Service. - > [!div class="nextstepaction"] > [Manage a bot](bot-service-manage-overview.md) diff --git a/articles/bot-service-reliability-guidance.md b/articles/bot-service-reliability-guidance.md new file mode 100644 index 000000000..668cbc73c --- /dev/null +++ b/articles/bot-service-reliability-guidance.md @@ -0,0 +1,21 @@ +--- +title: Reliability in Azure AI Bot Service +description: Learn where to find reliability guidance for Azure AI Bot Service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen +--- + +# Reliability guidance for Azure AI Bot Service + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +For guidance about reliability support in Azure AI Bot Service, see [What is reliability in Azure AI Bot Service](/azure/reliability/reliability-bot). +The article includes information about regional reliability with availability zones and cross-region resiliency with disaster recovery for bots with local data residency. + +For more information on deploying bots with local data residency and regional compliance, see [Regionalization in Azure AI Bot Service](v4sdk/bot-builder-concept-regionalization.md). diff --git a/articles/bot-service-resources-app-insights-keys.md b/articles/bot-service-resources-app-insights-keys.md index 745649cbc..259fce71e 100644 --- a/articles/bot-service-resources-app-insights-keys.md +++ b/articles/bot-service-resources-app-insights-keys.md @@ -1,22 +1,25 @@ --- title: Application Insights keys - Bot Service -description: Learn how to get Application Insights keys to add telemetry to a bot. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/19/2019 - +description: Learn how to add telemetry to a bot. See how to create the keys that you need to view data that Azure Application Insights collects about your application. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Application Insights keys -Azure **Application Insights** displays data about your application in a Microsoft Azure resource. To add telemetry to your bot you need an Azure subscription and an Application Insights resource created for your bot. From this resource, you can obtain the three keys to configure your bot: +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +Azure **Application Insights** displays data about your application in a Microsoft Azure resource. To add telemetry to your bot, you need an Azure subscription and an Application Insights resource created for your bot. From this resource, you can obtain the three keys to configure your bot: 1. Instrumentation key -2. Application ID -3. API key +1. Application ID +1. API key This topic will show you how to create these Application Insights keys. @@ -25,42 +28,49 @@ This topic will show you how to create these Application Insights keys. ## Instrumentation key -To get the Instrumentation key, do the following: +To get the Instrumentation key: + 1. From the [Azure portal](https://portal.azure.com), under the Monitor section, create a new **Application Insights** resource (or use an existing one). -![Portal screen capture of Application Insights listing](~/media/portal-app-insights-add-new.png) -2. From the list of Application Insights resources, click the Application Insight resource you just created. + :::image type="content" source="media/portal-app-insights-add-new.png" alt-text="Portal screen capture of Application Insights listing."::: + +1. From the list of Application Insights resources, click the Application Insight resource you created. + +1. Click **Overview**. -3. Click **Overview**. +1. Expand the **Essentials** block and find the **Instrumentation Key**. -4. Expand the **Essentials** block and find the **Instrumentation Key**. -![Portal screen capture of Overview](~/media/portal-app-insights-instrumentation-key-dropdown.png) -![Portal screen capture of the Instrumentation key](~/media/portal-app-insights-instrumentation-key.png) + :::image type="content" source="media/portal-app-insights-instrumentation-key-dropdown.png" alt-text="Portal screen capture of Overview"::: -5. Copy the **Instrumentation Key** and paste it to the **Application Insights Instrumentation Key** field of your bot's settings. + :::image type="content" source="media/portal-app-insights-instrumentation-key.png" alt-text="Portal screen capture of the Instrumentation key"::: + +1. Copy the **Instrumentation Key** and paste it to the **Application Insights Instrumentation Key** field of your bot's settings. ## Application ID -To get the Application ID, do the following: +To get the Application ID: + 1. From your Application Insights resource, click **API Access**. -2. Copy the **Application ID** and paste it to the **Application Insights Application ID** field of your bot's settings. -![Portal screen capture of the Application ID](~/media/portal-app-insights-appid.png) +1. Copy the **Application ID** and paste it to the **Application Insights Application ID** field of your bot's settings. + + :::image type="content" source="media/portal-app-insights-appid.png" alt-text="Portal screen capture of the Application ID"::: ## API key -To get the API key, do the following: +To get the API key: + 1. From the Application Insights resource, click **API Access**. +1. Click **Create API Key**. +1. Enter a short description, check the **Read telemetry** option, and click the **Generate key** button. -2. Click **Create API Key**. + :::image type="content" source="media/portal-app-insights-appid-apikey.png" alt-text="Portal screen capture of the Application ID and API Key"::: -3. Enter a short description, check the **Read telementry** option, and click the **Generate key** button. -![Portal screen capture of the Application ID and API Key](~/media/portal-app-insights-appid-apikey.png) + > [!WARNING] + > Copy this **API key** and save it because this key will never be shown to you again. If you lose this key, you'll need to create a new one. - > [!WARNING] - > Copy this **API key** and save it because this key will never be shown to you again. If you lose this key, you have to create a new one. +1. Copy the API key to the **Application Insights API key** field of your bot's settings. -4. Copy the API key to the **Application Insights API key** field of your bot's settings. +## Additional information -## Additional resources -For more information on how to connect these fields into your bot's settings, see [Enable analytics](~/bot-service-manage-analytics.md#enable-analytics). +For more information on how to connect these fields into your bot's settings, see [Enable analytics](bot-service-manage-analytics.md#enable-analytics). diff --git a/articles/bot-service-resources-bot-framework-faq.md b/articles/bot-service-resources-bot-framework-faq.md deleted file mode 100644 index 06a2130a5..000000000 --- a/articles/bot-service-resources-bot-framework-faq.md +++ /dev/null @@ -1,281 +0,0 @@ ---- -title: Bot Service Frequently Asked Questions - Bot Service -description: A list of Frequently Asked Questions about elements of the Bot Framework and when new features will become available. -author: scheyal -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/19/2020 ---- - -# Bot Framework Frequently Asked Questions - -This article contains answers to some frequently asked questions about the Bot Framework. - -## Background and availability - -### Why did Microsoft develop the Bot Framework? - -While the Conversation User Interface (CUI) is upon us, at this point few developers have the expertise and tools needed to create new conversational experiences or enable existing applications and services with a conversational interface their users can enjoy. We have created the Bot Framework to make it easier for developers to build and connect great bots to users, wherever they converse, including on Microsoft's premier channels. - -### What is the v4 SDK? -Bot Framework v4 SDK builds on the feedback and learnings from the prior Bot Framework SDKs. It introduces the right levels of abstraction while enabling rich componentization of the bot building blocks. You can start with a simple bot and grow your bot in sophistication using a modular and extensible framework. You can find [FAQ](https://github.com/Microsoft/botbuilder-dotnet/wiki/FAQ) for the SDK on GitHub. - -### Running a bot offline - - -Before talking about the use of a bot offline, meaning a bot not deployed on Azure or on some other host services but on premises, let's clarify a few points. - -- A bot is a web service that does not have a UI, so the user must interact with it via other means, in the form of channels, which use the [Azure Connector Service](rest-api/bot-framework-rest-connector-concepts.md#bot-connector-service). The connector functions as a *proxy* to relay messages between a client and the bot. -- The **connector** is a global application hosted on Azure nodes and spread geographically for availability and scalability. -- You use the [Bot Channel Registration](bot-service-quickstart-registration.md) to register the bot with the connector. - >[!NOTE] - > The bot must have its endpoint publicly reachable by the connector. - -You can run a bot offline with limited capabilities. For example, if you want to use a bot offline that has LUIS capability, you must build a container for the bot, and required tools, and a container for LUIS. Both connected via Docker Compose bridged network. - -This is a "partial" offline solution because a Cognitive Services container needs periodic online connection. - -> [!NOTE] -> The QnA service is not supported in a bot running offline. - -For more information, see: - -- [Deploy the Language Understanding (LUIS) container to Azure Container Instances](https://docs.microsoft.com/azure/cognitive-services/luis/deploy-luis-on-container-instances) -- [Container support in Azure Cognitive Services](https://docs.microsoft.com/azure/cognitive-services/cognitive-services-container-support) - -## Bot Framework SDK Version 3 Lifetime Support and Deprecation Notice -Microsoft Bot Framework SDK V4 was released in September 2018, and since then we have shipped a few dot-release improvements. As announced previously, the V3 SDK is being retired. Accordingly, there will be no more development in V3 repositories. **Existing V3 bot workloads will continue to run without interruption. We have no plans to disrupt any running workloads**. - -As mentioned, Bot Builder SDK V3 bots continue to run and be supported by Azure Bot Service. Bot Builder SDK V3 will only be supported for critical security bug fixes, connector, and protocol layer compatibility updates. - -All new features and capabilities are developed exclusively on [Bot Framework SDK V4](https://github.com/microsoft/botframework-sdk). Customers are encouraged to migrate their bots to V4 as soon as possible. - -We highly recommend that you start migrating your V3 bots to V4. In order to support this migration we have produced migration documentation and will provide extended support for migration initiatives (via standard channels such as Stack Overflow and Microsoft Customer Support). - - -For more information please refer to the following references: -* [Essential Migration Guidance](https://aka.ms/bf-migration-overview) -* Primary V4 Repositories to develop Bot Framework bots - * [Botbuilder for dotnet](https://github.com/microsoft/botbuilder-dotnet) - * [Botbuilder for JS](https://github.com/microsoft/botbuilder-js) -* QnA Maker Libraries were replaced with the following V4 libraries: - * [Libraries for dotnet](https://github.com/Microsoft/botbuilder-dotnet/tree/master/libraries/Microsoft.Bot.Builder.AI.QnA) - * [Libraries for JS](https://github.com/Microsoft/botbuilder-js/blob/master/libraries/botbuilder-ai/src/qnaMaker.ts) -* Azure Libraries were replaced with the following V4 libraries: - * [Botbuilder for JS Azure](https://github.com/Microsoft/botbuilder-js/tree/master/libraries/botbuilder-azure) - * [Botbuilder for dotnet Azure](https://github.com/Microsoft/botbuilder-dotnet/tree/master/libraries/Microsoft.Bot.Builder.Azure) - -### V3 Status Summary - -#### ABS Service -1. The ABS service side will continue to support running V3 bots with no planned end of life and any running bots will not be disrupted. -2. Channels will remain compatible with V3 with no disruption or end of life plan. -3. Creation of new V3 bots is disabled on the portal; however, expert users who wish to deploy their V3 bots independently, not on ABS (e.g. as webapp service) can do so. - -#### SDK and Tools -1. We are not investing in V3 from SDK side, and will only apply critical security fixes to the SDK branches for the foreseeable future (Exception: We plan to add a Skills connector to allow V4 bots to call legacy V3 bots). -2. SDKs and tools development is exclusively on V4 with no V3 work done or planned (hence we’re already “there”). -3. We do not prevent anyone from running old tools to manage their V3 bots. - - -## How can I migrate Azure Bot Service from one region to another? - -Azure Bot Service does not support region move. It’s a global service that is not tied to any specific region. - -## Channels -### When will you add more conversation experiences to the Bot Framework? - -We plan on making continuous improvements to the Bot Framework, including additional channels, but cannot provide a schedule at this time. -If you would like a specific channel added to the framework, [let us know][Support]. - -### I have a communication channel I’d like to be configurable with Bot Framework. Can I work with Microsoft to do that? - -We have not provided a general mechanism for developers to add new channels to Bot Framework, but you can connect your bot to your app via the [Direct Line API][DirectLineAPI]. If you are a developer of a communication channel and would like to work with us to enable your channel in the Bot Framework [we’d love to hear from you][Support]. - -### If I want to create a bot for Microsoft Teams, what tools and services should I use? - -The Bot Framework is designed to build, connect, and deploy high quality, responsive, performant and scalable bots for Teams and many other channels. The SDK can be used to create text/sms, image, button and card-capable bots (which constitute the majority of bot interactions today across conversation experiences) as well as bot interactions which are Teams-specific such as rich audio and video experiences. - -If you already have a great bot and would like to reach the Teams audience, your bot can easily be connected to Teams (or any supported channel) via the Bot Framework for REST API (provided it has an internet-accessible REST endpoint). - -### How do I create a bot that uses the US Government data center? - -There are 2 major steps required to create a bot that uses a US Government data center. -1. Add a “channel provider” setting in your appsettings.json (or the App Service Settings). This needs to be specifically set to this name/value constant: ChannelService = "https://botframework.azure.us". An example using appsetting.json is shown below. - -```json -{ - "MicrosoftAppId": "", - "MicrosoftAppPassword": "", - "ChannelService": "https://botframework.azure.us" -} -``` -2. If you are using .NET core, you will need to add a ConfigurationChannelProvider in your startup.cs file. How you do this varies based on which version of the SDK you are using. - -- For versions 4.3 and above, in your ConfigureServices method, you need to create a ConfigurationChannelProvider instance. When using the BotFrameworkHttpAdapter class, you inject this as singleton into the service collection like this: - -```csharp -services.AddSingleton(); -``` -- For versions prior to 4.3, in your ConfigureServices method, find the AddBot method. When setting the options, make sure you add: - -```csharp -options.ChannelProvider = new ConfigurationChannelProvider(); -``` -You can find more information concerning Govenment Services [here](https://docs.microsoft.com/azure/azure-government/documentation-government-services-aiandcognitiveservices#azure-bot-service) - -## Security and Privacy -### Do the bots registered with the Bot Framework collect personal information? If yes, how can I be sure the data is safe and secure? What about privacy? - -Each bot is its own service, and developers of these services are required to provide Terms of Service and Privacy Statements per their Developer Code of Conduct. You can access this information from the bot’s card in the Bot Directory. - -to provide the I/O service, the Bot Framework transmits your message and message content (including your ID), from the chat service you used to the bot. - -### Can I host my bot on my own servers? -Yes. Your bot can be hosted anywhere on the Internet. On your own servers, in Azure, or in any other datacenter. The only requirement is that the bot must expose a publicly-accessible HTTPS endpoint. - -### How do you ban or remove bots from the service? - -Users have a way to report a misbehaving bot via the bot’s contact card in the directory. Developers must abide by Microsoft terms of service to participate in the service. - -### Which specific URLs do I need to whitelist in my corporate firewall to access Bot Framework services? -If you have an outbound firewall blocking traffic from your bot to the Internet, you'll need to whitelist the following URLs in that firewall: -- login.botframework.com (Bot authentication) -- login.microsoftonline.com (Bot authentication) -- westus.api.cognitive.microsoft.com (for Luis.ai NLP integration) -- state.botframework.com (Bot state storage for prototyping) -- cortanabfchanneleastus.azurewebsites.net (Cortana channel) -- cortanabfchannelwestus.azurewebsites.net (Cortana Channel) -- *.botframework.com (channels) - -> [!NOTE] -> You may use `.botframework.com` if you’d prefer not to whitelist a URL with an asterisk. `` is equal to every channel your bot uses such as `directline.botframework.com`, `webchat.botframework.com`, and `slack.botframework.com`. It is also worthwhile to watch traffic over your firewall while testing the bot to make sure nothing else is getting blocked. - -### Can I block all traffic to my bot except traffic from the Bot Connector Service? -No. This sort of IP Address or DNS whitelisting is impractical. The Bot Framework Connector Service is hosted in Azure datacenters world-wide and the list of Azure IPs is constantly changing. Whitelisting certain IP addresses may work one day and break the next as the Azure IP Addresses change. - -### What keeps my bot secure from clients impersonating the Bot Framework Connector Service? -1. The security token accompanying every request to your bot has the ServiceUrl encoded within it, which means that even if an attacker gains access to the token, they cannot redirect the conversation to a new ServiceUrl. This is enforced by all implementations of the SDK and documented in our authentication [reference](https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-connector-authentication?view=azure-bot-service-3.0#bot-to-connector) materials. - -2. If the incoming token is missing or malformed, the Bot Framework SDK will not generate a token in response. This limits how much damage can be done if the bot is incorrectly configured. -3. Inside the bot, you can manually check the ServiceUrl provided in the token. This makes the bot more fragile in the event of service topology changes so this is possible but not recommended. - - -Note that these are outbound connections from the bot to the Internet. There is not a list of IP Addresses or DNS names that the Bot Framework Connector Service will use to talk to the bot. Inbound IP Address whitelisting is not supported. - -## Rate limiting -### What is rate limiting? -The Bot Framework service must protect itself and its customers against abusive call patterns (e.g., denial of service attack), so that no single bot can adversely affect the performance of other bots. To achieve this kind of protection, we’ve added rate limits (also known as throttling) to all endpoints. By enforcing a rate limit, we can restrict the frequency with which a bot can make a specific call. For example: with rate limiting enabled, if a bot wanted to post a large number of activities, it would have to space them out over a time period. Please note that the purpose of rate-limiting is not to cap the total volume for a bot. It is designed to prevent abuse of the conversational infrastructure that does not follow human conversation patterns. - -### How will I know if I’m impacted? -It is unlikely you’ll experience rate limiting, even at high volume. Most rate limiting would only occur due to bulk sending of activities (from a bot or from a client), extreme load testing, or a bug. When a request is throttled, an HTTP 429 (Too Many Requests) response is returned along with a Retry-After header indicating the amount of time (in seconds) to wait before retrying the request would succeed. You can collect this information by enabling analytics for your bot via Azure Application Insights. Or, you can add code in your bot to log messages. - -### How does rate limiting occur? -It can happen if: -- a bot sends messages too frequently -- a client of a bot sends messages too frequently -- Direct Line clients request a new Web Socket too frequently - -### What are the rate limits? -We’re continuously tuning the rate limits to make them as lenient as possible while at the same time protecting our service and our users. Because thresholds will occasionally change, we aren’t publishing the numbers at this time. If you are impacted by rate limiting, feel free to reach out to us at [bf-reports@microsoft.com](mailto://bf-reports@microsoft.com). - -## Related Services -### How does the Bot Framework relate to Cognitive Services? - -Both the Bot Framework and [Cognitive Services](https://www.microsoft.com/cognitive) are new capabilities introduced at [Microsoft Build 2016](https://build.microsoft.com) that will also be integrated into Cortana Intelligence Suite at GA. Both these services are built from years of research and use in popular Microsoft products. These capabilities combined with ‘Cortana Intelligence’ enable every organization to take advantage of the power of data, the cloud and intelligence to build their own intelligent systems that unlock new opportunities, increase their speed of business and lead the industries in which they serve their customers. - -### What is Cortana Intelligence? - -Cortana Intelligence is a fully managed Big Data, Advanced Analytics and Intelligence suite that transforms your data into intelligent action. -It is a comprehensive suite that brings together technologies founded upon years of research and innovation throughout Microsoft (spanning advanced analytics, machine learning, big data storage and processing in the cloud) and: - -* Allows you to collect, manage and store all your data that can seamlessly and cost effectively grow over time in a scalable and secure way. -* Provides easy and actionable analytics powered by the cloud that allow you to predict, prescribe and automate decision making for the most demanding problems. -* Enables intelligent systems through cognitive services and agents that allow you to see, hear, interpret and understand the world around you in more contextual and natural ways. - -With Cortana Intelligence, we hope to help our enterprise customers unlock new opportunities, increase their speed of business and be leaders in their industries. - -### What is the Direct Line channel? - -Direct Line is a REST API that allows you to add your bot into your service, mobile app, or webpage. - -You can write a client for the Direct Line API in any language. Simply code to the [Direct Line protocol][DirectLineAPI], generate a secret in the Direct Line configuration page, and talk to your bot from wherever your code lives. - -Direct Line is suitable for: - -* Mobile apps on iOS, Android, and Windows Phone, and others -* Desktop applications on Windows, OSX, and more -* Webpages where you need more customization than the [embeddable Web Chat channel][WebChat] offers -* Service-to-service applications - - -## App Registration - -### I need to manually create my App Registration. How do I create my own App Registration? - -Creating your own App Registration will be necessary for situations like the following: - -- You created your bot in the Bot Framework portal (such as https://dev.botframework.com/bots/new) -- You are unable to make app registrations in your organization and need another party to create the App ID for the bot you're building -- You otherwise need to manually create your own App ID (and password) - -To create your own App ID, follow the steps below. - -1. Sign into your [Azure account](https://portal.azure.com). If you don't have an Azure account, you can [sign up for a free account](https://azure.microsoft.com/free/). -2. Go to [the app registrations blade](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) and click **New registration** in the action bar at the top. - - ![new registration](media/app-registration/new-registration.png) - -3. Enter a display name for the application registration in the *Name* field and select the supported account types. The name does not have to match the bot ID. - - > [!IMPORTANT] - > In the *Supported account types*, select the *Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)* radio button. If any of the other options are selected, **the bot will be unusable**. - - ![registration details](media/app-registration/registration-details.png) - -4. Click **Register** - - After a few moments, the newly created app registration should open a blade. Copy the *Application (client) ID* in the Overview blade and paste it in to the App ID field. - - ![application id](media/app-registration/app-id.png) - -If you’re creating your bot through the Bot Framework portal, then you’re done setting up your app registration; the secret will be generated automatically. - -If you’re making your bot in the Azure portal, you need to generate a secret for your app registration. - -1. Click on **Certificates & secrets** in the left navigation column of your app registration’s blade. -2. In that blade, click the **New client secret** button. In the dialog that pops up, enter an optional description for the secret and select **Never** from the Expires radio button group. - - ![new secret](media/app-registration/new-secret.png) - -3. Copy your secret’s value from the table under *Client secrets* and paste it into the *Password* field for your application, and click **OK** at the bottom of that blade. Then, proceed with the bot creation. - - > [!NOTE] - > The secret will only be visible while on this blade, and you won't be able to retreive it after you leave that page. Be sure to copy it somewhere safe. - - ![new app id](media/app-registration/create-app-id.png) - - -[DirectLineAPI]: https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts -[Support]: bot-service-resources-links-help.md -[WebChat]: bot-service-channel-connect-webchat.md - -## Resources -### Which RBAC role is required to create and deploy a bot? - -Creating a bot in the Azure portal requires Contributor access either in the subscription or in a specific resource group. A user with the *Contributor* role in a resource group can create a new bot in that specific resource group. A user in the *Contributor* role for a subscription can create a bot in a new or existing resource group. - -Using the Azure CLI, a role-based access control approach can support custom roles. If you want to make a custom role with more constrained permissions, the following set will allow the user to create and deploy a bot that also supports LUIS, QnA Maker, and Application Insights. - - "Microsoft.Web/*", - "Microsoft.BotService/*", - "Microsoft.Storage/*", - "Microsoft.Resources/deployments/*", - "Microsoft.CognitiveServices/*", - "Microsoft.Search/searchServices/*", - "Microsoft.Insights/*", - "Microsoft.Insights/components/*" - -LUIS and QnA Maker require Cognitive Services permissions. QnA Maker also requires Search permissions. When creating a custom role, remember that any inherited *deny* permissions will supercede these *allow* permissions. diff --git a/articles/bot-service-resources-bot-framework-faq.yml b/articles/bot-service-resources-bot-framework-faq.yml new file mode 100644 index 000000000..9ea55de56 --- /dev/null +++ b/articles/bot-service-resources-bot-framework-faq.yml @@ -0,0 +1,98 @@ +### YamlMime:FAQ + +metadata: + title: Bot Framework Frequently Asked Questions Index - Bot Service + description: Frequently Asked Questions Bot Framework Index. + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +title: Bot Framework FAQ +summary: | + [!INCLUDE [applies-to-v4](./includes/applies-to-v4-current.md)] + + + + The following are some common questions you might have. + In case you don't find the answer you're looking for, you can post your questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) using the `botframework` tag. If you're a new user, visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask) first. + +sections: + - name: Ignored + questions: + - question: | + Background and availability + answer: | + - [Why did Microsoft develop the Bot Framework?](./bot-service-resources-faq-availability.yml#why-did-microsoft-develop-the-bot-framework) + - [How to run a bot offline?](./bot-service-resources-faq-availability.yml#how-to-run-a-bot-offline) + - [How can I migrate Azure AI Bot Service from one region to another?](./bot-service-resources-faq-availability.yml#how-can-i-migrate-azure-ai-bot-service-from-one-region-to-another) + - [What is the v4 SDK?](./bot-service-resources-faq-availability.yml#what-is-the-v4-sdk) + - [Bot Framework SDK Version 3 Lifetime Support and Deprecation Notice](./bot-service-resources-faq-availability.yml#bot-framework-sdk-version-3-lifetime-support-and-deprecation-notice) + + - question: | + Bot Framework general + answer: | + - [Why doesn't the Typing activity do anything?](./bot-service-resources-faq-general.yml#why-doesn-t-the-typing-activity-do-anything) + - [What is the difference between the Connector library and Builder library in the SDK?](./bot-service-resources-faq-general.yml#what-is-the-difference-between-the-connector-library-and-builder-library-in-the-sdk) + - [How do user messages relate to HTTPS method calls?](./bot-service-resources-faq-general.yml#how-do-user-messages-relate-to-https-method-calls) + - [What is the difference between "proactive" and "reactive"?](./bot-service-resources-faq-general.yml#what-is-the-difference-between--proactive--and--reactive-) + - [How can I send proactive messages to the user?](./bot-service-resources-faq-general.yml#how-can-i-send-proactive-messages-to-the-user) + - [What is an ETag? How does it relate to bot data bag storage?](./bot-service-resources-faq-general.yml#what-is-an-etag---how-does-it-relate-to-bot-data-bag-storage) + - [What is rate limiting?](./bot-service-resources-faq-general.yml#what-is-rate-limiting) + - [How does rate limiting occur?](./bot-service-resources-faq-general.yml#how-does-rate-limiting-occur) + - [What are the rate limits?](./bot-service-resources-faq-general.yml#what-are-the-rate-limits) + - [How will I know if I'm impacted by rate limiting?](./bot-service-resources-faq-general.yml#how-will-i-know-if-i-m-impacted-by-rate-limiting) + - [How to implement human handoff?](./bot-service-resources-faq-general.yml#how-to-implement-human-handoff) + - [What is the size limit of a file transferred using a channel?](./bot-service-resources-faq-general.yml#what-is-the-size-limit-of-a-file-transferred-using-channels) + + - question: | + Ecosystem + answer: | + - [How do I enable the Bot Framework Emulator to connect to localhost while behind a corporate proxy?](./bot-service-resources-faq-ecosystem.yml#how-do-i-enable-the-bot-framework-emulator-to-connect-to-localhost-while-behind-a-corporate-proxy) + - [When will you add more conversation experiences to the Bot Framework?](./bot-service-resources-faq-ecosystem.yml#when-will-you-add-more-conversation-experiences-to-the-bot-framework) + - [I have a communication channel I'd like to be configurable with Bot Framework. Can I work with Microsoft to do that?](./bot-service-resources-faq-ecosystem.yml#i-have-a-communication-channel-i-d-like-to-be-configurable-with-bot-framework--can-i-work-with-microsoft-to-do-that) + - [If I want to create a bot for Microsoft Teams, what tools and services should I use?](./bot-service-resources-faq-ecosystem.yml#if-i-want-to-create-a-bot-for-microsoft-teams--what-tools-and-services-should-i-use) + - [How do I create a bot that uses the US Government data center?](./bot-service-resources-faq-ecosystem.yml#how-do-i-create-a-bot-that-uses-the-us-government-data-center) + - [What is the Direct Line channel?](./bot-service-resources-faq-ecosystem.yml#what-is-the-direct-line-channel) + - [How does the Bot Framework relate to Azure AI services?](./bot-service-resources-faq-ecosystem.yml#how-does-the-bot-framework-relate-to-azure-cognitive-services) + - [What are the steps to configure Web Chat and Direct Line for Azure Government?](./bot-service-resources-faq-ecosystem.yml#what-are-the-steps-to-configure-web-chat-and-direct-line-for-azure-government) + - [What are the possible machine-readable resolutions of the LUIS built-in date, time, duration, and set entities?](./bot-service-resources-faq-ecosystem.yml#what-are-the-possible-machine-readable-resolutions-of-the-luis-built-in-date--time--duration--and-set-entities) + - [How can I use more than the maximum number of LUIS intents?](./bot-service-resources-faq-ecosystem.yml#how-can-i-use-more-than-the-maximum-number-of-luis-intents) + - [How can I use more than one LUIS model?](./bot-service-resources-faq-ecosystem.yml#how-can-i-use-more-than-one-luis-model) + - [Where can I get more help on LUIS?](./bot-service-resources-faq-ecosystem.yml#where-can-i-get-more-help-on-luis) + + - question: | + Security and Privacy + answer: | + - [Do the bots registered with the Bot Framework collect personal information? If yes, how can I be sure the data is safe and secure? What about privacy?](./bot-service-resources-faq-security.yml#do-the-bots-registered-with-the-bot-framework-collect-personal-information--if-yes--how-can-i-be-sure-the-data-is-safe-and-secure--what-about-privacy) + - [Can I host my bot on my own servers?](./bot-service-resources-faq-security.yml#can-i-host-my-bot-on-my-own-servers) + - [How do you ban or remove bots from the service?](./bot-service-resources-faq-security.yml#how-do-you-ban-or-remove-bots-from-the-service) + - [How does the Bot Framework handle identity and access management?](./bot-service-resources-faq-security.yml#how-does-the-bot-framework-handle-identity-and-access-management) + - [How do I restrict the use of my bot to users belonging to my tenant only?](./bot-service-resources-faq-security.yml#how-do-i-restrict-the-use-of-my-bot-to-users-belonging-to-my-tenant-only) + - [Which specific URLs do I need to allowlist in my corporate firewall to access Bot Framework services?](./bot-service-resources-faq-security.yml#which-specific-urls-do-i-need-to-allowlist-in-my-corporate-firewall-to-access-bot-framework-services) + - [Can I block all traffic to my bot except traffic from the Bot Framework Service?](./bot-service-resources-faq-security.yml#can-i-block-all-traffic-to-my-bot-except-traffic-from-the-bot-framework-service) + - [Which RBAC role is required to create and deploy a bot?](./bot-service-resources-faq-security.yml#which-rbac-role-is-required-to-create-and-deploy-a-bot) + - [What keeps my bot secure from clients impersonating the Bot Framework Service?](./bot-service-resources-faq-security.yml#what-keeps-my-bot-secure-from-clients-impersonating-the-bot-framework-service) + - [What is the purpose of the magic code during authentication?](./bot-service-resources-faq-security.yml#what-is-the-purpose-of-the-magic-code-during-authentication) + + - question: | + Azure + answer: | + - [How do I create my own App Registration?](./bot-service-resources-faq-azure.yml#how-do-i-create-my-own-app-registration) + - [What files do I need to zip up for deployment?](./bot-service-resources-faq-azure.yml#what-files-do-i-need-to-zip-up-for-deployment) + - [What version of Azure CLI should I use to deploy a bot?](./bot-service-resources-faq-azure.yml#what-version-of-azure-cli-should-i-use-to-deploy-a-bot) + - [What should I do when getting Azure CLI deprecation errors?](./bot-service-resources-faq-azure.yml#what-should-i-do-when-getting-azure-cli-deprecation-errors) + - [What are the CLI deprecated commands related to `az deployment?`](./bot-service-resources-faq-azure.yml#what-are-the-cli-deprecated-commands-related-to--az-deployment-) + - [How do I know whether the Azure CLI commands are deprecated?](./bot-service-resources-faq-azure.yml#how-do-i-know-whether-the-azure-cli-commands-are-deprecated) + - [What is the Azure Bot Resource?](./bot-service-resources-faq-azure.yml#what-is-the-azure-bot-resource) + - [Why are Web App Bot and Bot Channel Registration being deprecated?](./bot-service-resources-faq-azure.yml#why-are-web-app-bot-and-bot-channel-registration-being-deprecated) + - [Will my Web App Bot or Bot Channel Registration keep working and for how long?](./bot-service-resources-faq-azure.yml#will-my-web-app-bot-or-bot-channel-registration-keep-working-and-for-how-long) + - [Do I need to migrate my existing bot to the Azure Bot?](./bot-service-resources-faq-azure.yml#do-i-need-to-migrate-my-existing-bot-to-the-azure-bot) + - [Can I migrate my existing bot resource to an Azure Bot?](./bot-service-resources-faq-azure.yml#can-i-migrate-my-existing-bot-resource-to-an-azure-bot) + - [What's the difference between the Azure Bot and the Web App Bot or Bot Channels Registration?](./bot-service-resources-faq-azure.yml#what-s-the-difference-between-the-azure-bot-and-the-web-app-bot-or-bot-channels-registration) diff --git a/articles/bot-service-resources-faq-availability.yml b/articles/bot-service-resources-faq-availability.yml new file mode 100644 index 000000000..c628ffc4d --- /dev/null +++ b/articles/bot-service-resources-faq-availability.yml @@ -0,0 +1,105 @@ +### YamlMime:FAQ + +metadata: + title: Bot Framework Availability Frequently Asked Questions # Required; page title displayed in search results. Include the brand. < 60 chars. + description: "Answers to common availability frequent asked questions." # Required; article description that is displayed in search results. < 160 chars. + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +title: Bot Framework Availability Frequently Asked Questions +summary: | + This article answers commonly asked availability questions. + + + + [!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +sections: + - name: Availability + questions: + - question: Why did Microsoft develop the Bot Framework? + answer: | + We created the Bot Framework to make it easier for developers to build and connect great bots to users, wherever they converse, + including on Microsoft's premier channels. + + - question: How can I migrate Azure AI Bot Service from one region to another? + answer: | + Azure AI Bot Service doesn't support region move. It's a global service that isn't tied to any specific region. + + - question: How to run a bot offline? + answer: | + Before talking about the use of a bot offline, meaning a bot not deployed on Azure or on some other host services but on premises, let's clarify a few points. + - A bot is a web service that doesn't have a UI, so the user must interact with it via other means, in the form of channels, + which use the [Bot Framework Service](rest-api/bot-framework-rest-connector-concepts.md). + The connector functions as a *proxy* to relay messages between a client and the bot. + - The **connector** is a global application hosted on Azure nodes and spread geographically for availability and scalability. + - You use the [Bot Channel Registration](bot-service-quickstart-registration.md) to register the bot with the connector. + + >[!NOTE] + > The bot must have its endpoint publicly reachable by the connector. + + You can run a bot offline with limited capabilities. For example, if you want to use a bot offline that uses Azure AI services, + you must build a container for the bot, and required tools, and a container for the Azure AI services. Both connected via Docker Compose bridged network. + This is a "partial" offline solution because an Azure AI services container needs periodic online connection. + + > [!NOTE] + > [Azure AI QnA Maker will be retired on 31 March 2025](https://azure.microsoft.com/updates/azure-qna-maker-will-be-retired-on-31-march-2025/). + > Beginning 1 October 2022, you won't be able to create new QnA Maker resources or knowledge bases. + > + > [Language Understanding (LUIS) will be retired on 1 October 2025](https://azure.microsoft.com/updates/language-understanding-retirement/). + > Beginning 1 April 2023, you won't be able to create new LUIS resources. + > + > Newer versions of theses services are now available as part of Azure AI Language. + > For more information about question-and-answer and language understanding support in the Bot Framework SDK, + > see [Natural language understanding](./v4sdk/bot-builder-concept-luis.md). + + For more information, see: + - [Deploy the Language Understanding (LUIS) container to Azure Container Instances](/azure/ai-services/containers/azure-container-instance-recipe) + - [Container support in Azure AI services](/azure/ai-services/cognitive-services-container-support) + + - question: What is the v4 SDK? + answer: | + Bot Framework v4 SDK builds on the feedback and learnings from the prior Bot Framework SDK versions. + It introduces the right levels of abstraction while enabling rich set of components as the bot building blocks. + You can start with a simple bot and grow it in sophistication using a modular and extensible framework. + See also [What's new with Bot Framework](https://github.com/microsoft/botbuilder-dotnet#whats-new-with-bot-framework) on GitHub. + + - question: Bot Framework SDK Version 3 Lifetime Support and Deprecation Notice + answer: | + Microsoft Bot Framework SDK V4 was released in September 2018, and since then we've shipped a few dot-release improvements. + As announced previously, the V3 SDK is being retired. Accordingly, there will be no more development in V3 repositories. + **Existing V3 bot workloads will continue to run without interruption. We have no plans to disrupt any running workloads**. + + As mentioned, Bot Builder SDK V3 bots continue to run and be supported by Azure AI Bot Service. + Bot Builder SDK V3 will only be supported for critical security bug fixes, connector, and protocol layer compatibility updates. + + All new features and capabilities are developed exclusively on [Bot Framework SDK V4](https://github.com/microsoft/botframework-sdk). + Customers are encouraged to migrate their bots to V4 as soon as possible. + + ### Azure AI Bot Service + 1. The Azure AI Bot Service will continue to support the running of V3 bots with no planned end of life, and any running bots won't be disrupted. + 1. Channels will remain compatible with V3 with no disruption or end of life plan. + 1. Creation of new V3 bots is disabled on the portal; however, expert users who wish to deploy their V3 bots independently through other services (such as another web app service) can do so. + + ### SDK and Tools + 1. We won't be updating the V3 SDK for the foreseeable future, except to apply critical security fixes and to add a skills connector to allow V4 bots to call legacy V3 bots. + 1. SDKs and tools development is exclusively on V4 with no V3 work done or planned (hence we're already "there"). + 1. We don't prevent anyone from running old tools to manage their V3 bots. + + ### References + - [How bots work](v4sdk/bot-builder-basics.md) + - Primary V4 Repositories to develop Bot Framework bots + - [Bot Framework SDK for .NET](https://github.com/microsoft/botbuilder-dotnet) + - [Bot Framework SDK for JavaScript](https://github.com/microsoft/botbuilder-js) + - QnA Maker Libraries were replaced with the following V4 libraries + - [Microsoft.Bot.Builder.AI.QnA](https://github.com/Microsoft/botbuilder-dotnet/tree/master/libraries/Microsoft.Bot.Builder.AI.QnA) + - [botbuilder-ai](https://github.com/Microsoft/botbuilder-js/blob/master/libraries/botbuilder-ai/src/qnaMaker.ts) + - Azure Libraries were replaced with the following V4 libraries + - [botbuilder-azure](https://github.com/Microsoft/botbuilder-js/tree/master/libraries/botbuilder-azure) + - [Microsoft.Bot.Builder.Azure](https://github.com/Microsoft/botbuilder-dotnet/tree/master/libraries/Microsoft.Bot.Builder.Azure) diff --git a/articles/bot-service-resources-faq-azure.yml b/articles/bot-service-resources-faq-azure.yml new file mode 100644 index 000000000..20cb52bc0 --- /dev/null +++ b/articles/bot-service-resources-faq-azure.yml @@ -0,0 +1,165 @@ +### YamlMime:FAQ + +metadata: + title: Bot Framework Azure Frequently Asked Questions + description: "Answers to common Azure frequently asked questions." + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +title: Bot Framework Azure Frequently Asked Questions # < 60 chars +summary: | + This article answers commonly asked Azure questions. + + + + [!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +sections: + - name: Azure + questions: + - question: How do I create my own App Registration? + answer: | + You might create your own **App Registration** when: + + - You create your bot in the [Bot Framework portal](https://dev.botframework.com/bots/new). + - You create the app registration for a third party that doesn't have access to Azure. + - You need to manually create your own App ID (and password). + + Follow the steps described below. + + 1. Sign into your [Azure account](https://portal.azure.com). If you don't have an Azure account, you can [sign up for a free account](https://azure.microsoft.com/free/). + 1. Go to [the app registrations pane](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) and click **New registration** in the action bar at the top. + + :::image type="content" source="media/app-registration/new-registration.png" alt-text="new registration"::: + + 1. Enter a display name for the application registration in the *Name* field and select the supported account types. + + :::image type="content" source="media/app-registration/registration-details.png" alt-text="registration details"::: + + 1. Select **Register** + + After a few moments, the newly created app registration should open a pane. + Copy the *Application (client) ID* in the Overview pane and save it. + You'll need it to populate the related App ID field during the bot creation, as explained later in this article. + + :::image type="content" source="media/app-registration/app-id.png" alt-text="application id"::: + + ### Creating a bot using the Azure portal + + If you're creating your bot in the [Azure portal](https://ms.portal.azure.com/#home) using the **Azure Bot** resource, + you need to generate a secret key (client secret) for your app registration. + + 1. Select **Certificates & secrets** in the left navigation column of your app registration's pane. + 1. In that pane, on the right, select the **New client secret** button. + In the pop-up dialog, enter an optional description for the secret, select the expiration from the **Expires** radio button group. + + :::image type="content" source="media/app-registration/new-secret.png" alt-text="new secret"::: + + 1. Select **Add**. + 1. Copy the secret's value from the table under *Client secrets* and save it. + + :::image type="content" source="media/app-registration/new-secret-copy.png" alt-text="copy secret"::: + + 1. During your bot registration, when creating the App ID and password, enter the App ID and the secret you saved in the + proper input boxes as shown in the figure below. + + > [!NOTE] + > The secret will only be visible while on this blade, and you won't be able to retrieve it after you leave that page. Be sure to copy it somewhere safe. + + :::image type="content" source="media/app-registration/create-app-id.png" alt-text="new app id"::: + + An example of how to apply the above steps can be found in [Add authentication to a bot](v4sdk\bot-builder-authentication.md). + + - question: What files do I need to zip up for deployment? + answer: | + You must manually create a zip archive with all the files in the project, as described in the step: [zip up the code directory manually step](/azure/bot-service/bot-builder-deploy-az-cli?view=azure-bot-service-4.0&tabs=csharp#52-zip-up-the-code-directory-manually). + Make sure that you select all the files and folders in your bot's project folder. + Then, while still in the project folder, zip up all the selected files and folders as shown in the picture below. + + :::image type="content" source="./media/deploy-bot-cli/select-all-zip.png" alt-text="select all and zip"::: + + - question: What version of Azure CLI should I use to deploy a bot? + answer: | + Use the latest version of the Azure Command-Line Interface (CLI) otherwise you'll get deprecated commands errors. + See [Install the Azure CLI](/cli/azure/install-azure-cli). + + - question: What should I do when getting Azure CLI deprecation errors? + answer: | + Upgrade to the latest version of the [Azure CLI](/cli/azure/install-azure-cli). + For Azure CLI version [2.2.0](https://github.com/MicrosoftDocs/azure-docs-cli/blob/master/docs-ref-conceptual/release-notes-azure-cli.md#march-10-2020) or later, + you must use `az deployment sub create` and `az deployment group create` instead of `az deployment create` and `az group deployment create` commands respectively. + + ### Change log of the Azure CLI commands used to deploy a bot to Azure + + |Azure ClI version | Command1 | Command 2| + |-------|-------|-------| + |Azure CLI 2.2.0 and later versions | `az deployment group create` | `az deployment sub create` | + |Azure CLI 2.1.0 and earlier versions | `az group deployment create` | `az deployment create` | + + For more information, see [Azure CLI change log](https://github.com/MicrosoftDocs/azure-docs-cli/blob/master/docs-ref-conceptual/release-notes-azure-cli.md). + + - question: What are the CLI deprecated commands related to `az deployment`? + answer: | + The following are the `az deployment` deprecated commands: + + > [!div class="mx-tdBreakAll"] + > |Deprecated|Replaced By| + > |-------------|----------| + > |`az deployment `|`az deployment **sub** `| + > | `az deployment operation ` |`az deployment operation **sub** `| + > | `az group deployment ` | `az deployment **group** ` | + > |`az group deployment operation ` |`az deployment operation **group** < list/show>` | + + - question: How do I know whether the Azure CLI commands are deprecated? + answer: | + To know if an Azure CLI command is deprecated, execute the command with the `-h` (help) option as shown in the picture below. + + :::image type="content" source="./media/deploy-bot-cli/cli-help.png" alt-text="cli help"::: + + ### Azure CLI Change Log + + Read more about [Azure CLI change log](https://github.com/MicrosoftDocs/azure-docs-cli/blob/master/docs-ref-conceptual/release-notes-azure-cli.md). + + ### Azure Resource Management (ARM) + + The following is a consolidated list of the commands that fit the current Azure CLI design: az {command group} {?sub-command-group} {operation} {parameters}. + + - `az resource`: Improve the examples of the resource module. + - `az policy assignment list`: Support listing policy assignments at Management Group scope. + - Add `az deployment group` and `az deployment operation group` for template deployment at resource groups. This is a duplicate of `az group deployment` and `az group deployment operation`. + - Add `az deployment sub` and `az deployment operation sub` for template deployment at subscription scope. This is a duplicate of `az deployment` and `az deployment operation`. + - Add `az deployment mg` and `az deployment operation mg` for template deployment at management groups. + - Add `ad deployment tenant` and `az deployment operation tenant` for template deployment at tenant scope. + - `az policy assignment create`: Add a description to the `--location` parameter. + - `az group deployment create`: Add parameter `--aux-tenants` to support cross tenants. + - question: What is the Azure Bot Resource? + answer: | + The Azure Bot resource is an update from the Web App Bot or Bot Channels Registration, which makes it easier for developers to get started using the Bot Framework Composer bot development tool. + Instead of creating resources such as a web app during bot creation, resources are created using Bot Framework Composer, which offers more flexibility in what resources are deployed. + Composer provides a visual design canvas that makes bot development easier. + - question: Why are Web App Bot and Bot Channel Registration being deprecated? + answer: | + Functionally, Azure Bot, Web App Bot, and Bot Channels Registration all utilize the Bot Service in the same way "under the hood". + The Azure Bot path is easier, and more configurable for developers. Legacy items are being removed from the Azure Marketplace, though existing resources will continue to run and be supported. + - question: Will my Web App Bot or Bot Channel Registration keep working and for how long? + answer: | + Yes, your Web App Bot or Bot Channel Registration based resource will continue to run. + There are no plans to drop support for existing bot resources. + - question: Do I need to migrate my existing bot to the Azure Bot? + answer: | + No, you don't need to migrate your existing bot; it will continue to run as is. + - question: Can I migrate my existing bot resource to an Azure Bot? + answer: | + Currently, no, this isn't supported. If your bot isn't being used in production, you can delete your old bot resource and reuse the bot name and app ID in a new bot resource (after some time for the old record to get deleted). + The new Azure Bot is designed around creating your bot using Composer, and so the gains from such a migration would be minimal. + - question: What's the difference between the Azure Bot and the Web App Bot or Bot Channels Registration? + answer: | + The Bot Service behind Azure Bot and Web App Bot/Bot Channels Registration is the same; the change is in how the resources are created that you use in your bot such as web apps, language understanding, and more. + Rather than the one size fits all templates that the Web App Bot used, Bot Framework Composer can be used to create resources for the Bot with much more flexibility than the Web App Bot templates allowed. + Bot Channels Registration and Azure Bot are basically the same capabilities renamed. The UX is slightly different in the Azure portal to help customers connect to the Bot Framework Composer. diff --git a/articles/bot-service-resources-faq-ecosystem.yml b/articles/bot-service-resources-faq-ecosystem.yml new file mode 100644 index 000000000..7e905bbe2 --- /dev/null +++ b/articles/bot-service-resources-faq-ecosystem.yml @@ -0,0 +1,140 @@ +### YamlMime:FAQ + +metadata: + title: Bot Framework Ecosystem Frequently Asked Questions + description: "Answers to common ecosystem frequently asked questions." + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +# Attention writers! +# - This article contains FAQs regarding Bot Framework Ecosystem. +# - When you create a new FAQ, please add the related link to the proper section in bot-service-resources-bot-framework-faq.yml. + +title: Bot Framework Ecosystem Frequently Asked Questions # < 60 chars +summary: | + [!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + + This article answers commonly asked ecosystem questions. + +sections: + - name: Ecosystem + questions: + - question: How do I enable the Bot Framework Emulator to connect to localhost while behind a corporate proxy? + answer: | + When developing a bot in a corporate environment, typically, the proxy will block the connection unless you specify that it should be bypassed for `localhost`. + To do that on your local machine you must define the following environment variable: + + ```cmd + NO_PROXY=localhost + ``` + For more information, see [Configure proxy settings](bot-service-debug-emulator.md#configure-proxy-settings). + + - question: When will you add more conversation experiences to the Bot Framework? + answer: | + We plan on making continuous improvements to the Bot Framework, including additional channels, but can't provide a schedule at this time. + If you would like a specific channel added to the framework, [let us know][Support]. + + - question: I have a communication channel I'd like to be configurable with Bot Framework. Can I work with Microsoft to do that? + answer: | + We haven't provided a general mechanism for developers to add new channels to Bot Framework, but you can connect your bot to your app via the [Direct Line API][DirectLineAPI]. + If you're a developer of a communication channel and would like to work with us to enable your channel in the Bot Framework, [we'd love to hear from you][Support]. + + - question: If I want to create a bot for Microsoft Teams, what tools and services should I use? + answer: | + The Bot Framework is designed to build, connect, and deploy high quality, responsive, performant and scalable bots for Teams and many other channels. + The SDK can be used to create text/sms, image, button and card-capable bots (that is, most bot interactions across conversation experiences) + as well as bot interactions that are Teams-specific, such as rich audio and video experiences. + + If you already have a great bot and would like to reach the Teams audience, your bot can easily be connected to Teams (or any supported channel) via the Bot Framework + for REST API (provided it has an internet-accessible REST endpoint). + + - question: How do I create a bot that uses the US Government data center? + answer: | + See how to [configure Bot Framework bots for US Government customers](how-to-deploy-gov-cloud-high.md). + + - question: What is the Direct Line channel? + answer: | + Direct Line is a REST API that allows you to add your bot into your service, mobile app, or webpage. + You can write a client for the Direct Line API in any language. Simply code to the [Direct Line protocol][DirectLineAPI], + generate a secret in the Direct Line configuration page, and talk to your bot from wherever your code lives. + + Direct Line is suitable for: + + - Mobile apps on iOS, Android, and Windows Phone, and others + - Desktop applications on Windows, OSX, and more + - Webpages where you need more customization than the [embeddable Web Chat channel][WebChat] offers + - Service-to-service applications + + - question: What are the steps to configure Web Chat and Direct Line for Azure Government? + answer: | + The steps to configure Web Chat and Direct Line for Azure Government are similar to those used for global Azure. + In Azure Government, you set the [domain](https://github.com/microsoft/BotFramework-WebChat/blob/master/packages/bundle/src/createDirectLine.js#L6) to the Azure Government URL because the [default domain](https://github.com/microsoft/BotFramework-DirectLineJS/blob/master/src/directLine.ts#L456) applies to global Azure, not Azure Government. + The global Azure URL (`https://webchat.botframework.com/v3/directline`) is different from Azure Government URL (`https://webchat.botframework.azure.us/v3/directline`) for the Web Chat and Direct Line configuration. + The following example shows how to set the domain to the Azure Government URL: + + ```html + +
+ + + ``` + Learn more from the following docs: + + - [Connect a bot to Web Chat](bot-service-channel-connect-webchat.md) + - [Connect a bot to Direct Line](bot-service-channel-connect-directline.md) + - For programmatic approach to exchange your secret for a token, use the code snippet provided in the [production embedding option](bot-service-channel-connect-webchat.md#production-embedding-option) + and adjust the URLs from global Azure to Azure Government. + + - question: How does the Bot Framework relate to Azure cognitive services? + answer: | + Both the Bot Framework and [Azure AI services](/azure/ai-services/) are built from years of research and use in popular Microsoft products. + These capabilities enable every organization to take advantage of the power of data, the cloud and intelligence to build their own intelligent systems that unlock new opportunities, + increase their speed of business and lead the industries in which they serve their customers. + + - question: What are the possible machine-readable resolutions of the LUIS built-in date, time, duration, and set entities? + answer: | + [!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] + + For a list of examples, see the [Pre-built entities section](/azure/ai-services/LUIS/luis-reference-prebuilt-entities) of the LUIS documentation. + + - question: How can I use more than the maximum number of LUIS intents? + answer: | + [!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] + + You might consider splitting up your model and calling the LUIS service in series or parallel. + + - question: How can I use more than one LUIS model? + answer: | + [!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] + + Both the Bot Framework SDK for Node.js and the Bot Framework SDK for .NET support calling multiple LUIS models from a single LUIS intent dialog. Keep in mind the following caveats: + + * Using multiple LUIS models assumes the LUIS models have non-overlapping sets of intents. + * Using multiple LUIS models assumes the scores from different models are comparable, to select the "best matched intent" across multiple models. + * Using multiple LUIS models means that if an intent matches one model, it will also strongly match the "none" intent of the other models. You can avoid selecting the "none" intent in this situation; the Bot Framework SDK for Node.js will automatically scale down the score for "none" intents to avoid this issue. + + - question: Where can I get more help on LUIS? + answer: | + [!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] + + - [LUIS documentation](/azure/ai-services/luis/) + - [Language Understanding Forum](https://social.msdn.microsoft.com/forums/azure/home?forum=LUIS) diff --git a/articles/bot-service-resources-faq-general.yml b/articles/bot-service-resources-faq-general.yml new file mode 100644 index 000000000..0b923424c --- /dev/null +++ b/articles/bot-service-resources-faq-general.yml @@ -0,0 +1,102 @@ +### YamlMime:FAQ + +metadata: + title: General Frequently Asked Questions in Bot Framework SDK + description: Answers to common general frequently asked questions. + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +title: Bot Framework General Frequently Asked Questions +summary: | + This article answers commonly asked general questions. + + + + [!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +sections: + - name: General + questions: + - question: Why doesn't the Typing activity do anything? + answer: | + Some channels don't support transient typing updates in their client. + + - question: What is the difference between the Connector library and Builder library in the SDK? + answer: | + The Connector library is the exposition of the REST API. + The Builder library adds the conversational dialog programming model and other features such as prompts, waterfalls, chains, and guided form completion. + The Builder library also provides access to Azure AI services such as natural language understanding. + + - question: How do user messages relate to HTTPS method calls? + answer: | + When the user sends a message over a channel, the Bot Framework web service will issue an HTTPS POST to the bot's web service endpoint. + The bot may send zero, one, or many messages back to the user on that channel, by issuing a separate HTTPS POST to the Bot Framework + for each message that it sends. + + - question: What is the difference between "proactive" and "reactive"? + answer: | + From the perspective of your bot, "reactive" means that the user initiates the conversation by sending a message to the bot, and the bot reacts by responding to that message. + In contrast, "proactive" means that the bot initiates the conversation by sending the first message to the user. + For example, a bot may send a proactive message to notify a user when a timer expires or an event occurs. + + - question: How can I send proactive messages to the user? + answer: | + For examples that show how to send proactive messages, see the [C# V4 samples](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages) and + [Node.js V4 samples](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/16.proactive-messages) within the BotBuilder-Samples repository on GitHub. + + - question: What is an ETag? How does it relate to bot data bag storage? + answer: | + An [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) is a mechanism for [optimistic concurrency control](https://en.wikipedia.org/wiki/Optimistic_concurrency_control). + The bot data bag storage uses ETags to prevent conflicting updates to the data. An ETag error with HTTP status code 412 "Precondition Failed" indicates that there were + multiple messages received in parallel before the bot could finish its first operation. + The dialog stack and state are stored in bot data bags. For example, you might see the "Precondition Failed" ETag error if your bot is still processing a previous message + when it receives a new message for that conversation. + + - question: What is rate limiting? + answer: | + The Bot Framework service must protect itself and its customers against abusive call patterns (for example, a denial of service attack), so that no single bot can adversely affect the + performance of other bots. To achieve this kind of protection, we've added rate limits (also known as throttling) to our endpoints. By enforcing a rate limit, we can restrict the + frequency with which a client or bot can make a specific call. For example: with rate limiting enabled, if a bot wanted to post a large number of activities, it would have to space + them out over a time period. The purpose of rate-limiting isn't to cap the total volume for a bot. It's designed to prevent abuse of the conversational infrastructure + that doesn't follow human conversation patterns. For example, flooding two conversations with more content than two humans could ever consume. + + - question: What are the rate limits? + answer: | + We're continuously tuning the rate limits to make them as lenient as possible while at the same time protecting our service and our users. + Because thresholds will occasionally change, we aren't publishing the numbers at this time. Finally, if you're hosting your bot on an App Service, the bot is bound to the limitations of + the App Service. For more information, see [SLA summary for Azure services](https://azure.microsoft.com/support/legal/sla/summary/) If you're impacted by rate limiting, feel free to reach out + to us at [bf-reports@microsoft.com](mailto://bf-reports@microsoft.com). + + - question: What is the size limit of a file transferred using channels? + answer: | + Some channels have limits on the size or type of files that can be sent. For example, both Direct Line and Facebook limit activity payloads to **262,144 bytes**, while the Bot Framework Emulator has no limit. + Such limits are imposed by the channel. If you send a message that exceeds this limit, you may get an error, such as: *The request content length exceeded limit of 262,144 bytes*. + You can, however, provide a link to the resource as an internet attachment. For more information on sending attachments, see [how to add media to messages](v4sdk/bot-builder-howto-add-media-attachments.md). + + - question: How will I know if I'm impacted by rate limiting? + answer: | + It's unlikely you'll experience rate limiting, even at high volume. Most rate limiting would only occur due to bulk sending of activities (from a bot or from a client), extreme load testing, or a bug. + When a request is throttled, an HTTP 429 (Too Many Requests) response is returned along with a Retry-After header indicating the amount of time (in seconds) to wait before retrying the request would + succeed. You can collect this information by enabling analytics for your bot via Azure Application Insights. Or, you can add code in your bot to log messages. + + - question: How does rate limiting occur? + answer: | + Rate limiting is caused by any of the following conditions: + - A bot sends messages too frequently + - A client of a bot sends messages too frequently + - Direct Line clients request a new Web Socket too frequently + + - question: How to implement human handoff? + answer: | + At times, it's necessary to transfer (handoff) a conversation from a bot to a human being, such as when the bot doesn't understand the user or the request can't be automated. + In these cases, the bot provides a transition to humans. + The Bot Framework SDK supports handoff to a human. There a few **event types** for signaling handoff operations. + These events are exchanged between a **bot** and an **agent hub**, also called engagement hub. This agent hub is defined as an application or a system that allows agents, typically humans, + to receive and handle requests from users and escalation requests from bots. + For detailed information, see the [Transition conversations from bot to human](bot-service-design-pattern-handoff-human.md) article. diff --git a/articles/bot-service-resources-faq-security.yml b/articles/bot-service-resources-faq-security.yml new file mode 100644 index 000000000..d05d816f8 --- /dev/null +++ b/articles/bot-service-resources-faq-security.yml @@ -0,0 +1,168 @@ +### YamlMime:FAQ + +metadata: + title: Bot Framework Security and Privacy Frequently Asked Questions + description: "Answers to common security and privacy frequently asked questions." + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + ms.topic: faq + ms.service: azure-ai-bot-service + +title: Bot Framework Security and Privacy Frequently Asked Questions # < 60 chars +summary: | + This article answers commonly asked security and privacy questions. + + + + [!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +sections: + - name: Azure + questions: + - question: Do the bots registered with the Bot Framework collect personal information? + If yes, how can I be sure the data is safe and secure? What about privacy? + answer: | + Each bot is its own service, and developers of these services are required to provide Terms of Service and Privacy Statements per their + Developer Code of Conduct. For more information, see [bot review guidelines](bot-service-review-guidelines.md). + + - question: Can I host my bot on my own servers? + answer: | + Yes. Your bot can be hosted anywhere on the Internet. On your own servers, in Azure, or in any other data center. + The only requirement is that the bot must expose a publicly accessible HTTPS endpoint. + + - question: How do you ban or remove bots from the service? + answer: | + Users have a way to report a misbehaving bot via the bot's contact card in the directory. + Developers must abide by Microsoft terms of service to participate in the service. + + - question: Which specific URLs do I need to allowlist in my corporate firewall to access Bot Framework services? + answer: | + If you have an outbound firewall that blocks traffic from your bot to the Internet, you'll need to allowlist the following URLs in that firewall: + + - `login.botframework.com` (Bot authentication) + - `login.microsoftonline.com` (Bot authentication) + - `westus.api.cognitive.microsoft.com` (for Luis.ai NLP integration) + - `*.botframework.com` (channels) + - `state.botframework.com` (backward compatibility) + - `login.windows.net` (Windows login) + - `login.windows.com` (Windows login) + - `sts.windows.net` (Windows login) + - Other URLs for specific Bot Framework channels + + [!INCLUDE [luis-sunset-alert](./includes/luis-sunset-alert.md)] + + > [!NOTE] + > You may use `.botframework.com` if you'd prefer not to allowlist a URL with an asterisk. `` is equal to + > every channel your bot uses such as `directline.botframework.com`, `webchat.botframework.com`, and `slack.botframework.com`. + > It's also worthwhile to watch traffic over your firewall while testing the bot to see what traffic it's blocking. + + - question: Can I block all traffic to my bot except traffic from the Bot Framework Service? + answer: | + Bot Framework Services is hosted in Azure data centers world-wide and the list of Azure IPs is constantly changing. + That means allow-listing certain IP addresses may work one day and break the next as the Azure IP Addresses change. + + - question: Which RBAC role is required to create and deploy a bot? + answer: | + Creating a bot in the Azure portal requires Contributor access either in the subscription or in a specific resource group. + A user with the *Contributor* role in a resource group can create a new bot in that specific resource group. + A user in the *Contributor* role for a subscription can create a bot in a new or existing resource group. + + Using the Azure CLI, a role-based access control approach can support custom roles. + If you want to make a custom role with more constrained permissions, the set below allows the user to create and deploy + a bot that also supports LUIS, QnA Maker, and Application Insights. + + ```text + "Microsoft.Web/*", + "Microsoft.BotService/*", + "Microsoft.Storage/*", + "Microsoft.Resources/deployments/*", + "Microsoft.CognitiveServices/*", + "Microsoft.Search/searchServices/*", + "Microsoft.Insights/*", + "Microsoft.Insights/components/*" + ``` + + > [!NOTE] + > [Azure AI QnA Maker will be retired on 31 March 2025](https://azure.microsoft.com/updates/azure-qna-maker-will-be-retired-on-31-march-2025/). + > Beginning 1 October 2022, you won't be able to create new QnA Maker resources or knowledge bases. + > + > [Language Understanding (LUIS) will be retired on 1 October 2025](https://azure.microsoft.com/updates/language-understanding-retirement/). + > Beginning 1 April 2023, you won't be able to create new LUIS resources. + > + > Newer versions of theses services are now available as part of Azure AI Language. + > For more information about question-and-answer and language understanding support in the Bot Framework SDK, + > see [Natural language understanding](./v4sdk/bot-builder-concept-luis.md). + + > [!NOTE] + > LUIS and QnA Maker require Azure AI services permissions. QnA Maker also requires Search permissions. + > When creating a custom role, remember that any inherited *deny* permissions will supersede these *allow* permissions. + + - question: What keeps my bot secure from clients impersonating the Bot Framework Service? + answer: | + 1. All authentic Bot Framework requests are accompanied by a JWT token whose cryptographic signature can be verified by following the [authentication](rest-api/bot-framework-rest-connector-authentication.md) guide. + The token is designed so attackers can't impersonate trusted services. + 1. The security token accompanying every request to your bot has the ServiceUrl encoded within it, which means that even if an attacker gains access to the token, they can't redirect the conversation to a new ServiceUrl. + This is enforced by all implementations of the SDK and documented in our authentication [reference](rest-api/bot-framework-rest-connector-authentication.md#bot-to-connector) materials. + 1. If the incoming token is missing or malformed, the Bot Framework SDK won't generate a token in response. This limits how much damage can be done if the bot is incorrectly configured. + 1. Inside the bot, you can manually check the ServiceUrl provided in the token. This makes the bot more fragile if the service topology changes, so while this is possible, it's not recommended. + + > [!NOTE] + > These are outbound connections from the bot to the Internet. + > There isn't a list of IP Addresses or DNS names that the Bot Framework Connector Service will use to talk to the bot. + > Inbound IP Address allow-listing isn't supported. + + - question: What is the purpose of the magic code during authentication? + answer: | + In the Web Chat control, there are two mechanisms to assure that the proper user is signed in. + + 1. **Magic code**. At the end of the sign-in process, the user is presented with a randomly generated 6-digit code (*magic code*). + The user must type this code in the conversation to complete the sign-in process. This tends to result in a bad user's experience. + Additionally, it's still susceptible to phishing attacks. A malicious user can trick another user to sign-in and obtain the magic + code through phishing. + + > [!WARNING] + > The use of the magic code is deprecated. Instead, you should use **Direct Line enhanced authentication**, described below. + + 1. **Direct Line enhanced authentication**. Because of the issues with the *magic code* approach, Azure AI Bot Service removed its need. + Azure AI Bot Service guarantees that the sign-in process can only be completed in the **same browser session** as the Web Chat itself. + To enable this protection, you must start Web Chat with a **Direct Line token** that contains a list of **trusted origins**, + also know as trusted domains, that can host the bot's Web Chat client. + With enhanced authentication options, you can statically specify the list of trusted origins in the Direct Line configuration page. + For more information, see [Direct Line enhanced authentication](v4sdk/bot-builder-security-enhanced.md). + + - question: How does the Bot Framework handle identity and access management? + answer: | + The identity and access management (IAM), is a framework (policies and technologies) to allow proper people to have + appropriate access to technology resources. For more information, see [Identity management](https://en.wikipedia.org/wiki/Identity_management). + + The Bot Framework provides the following identification mechanisms: + + - **Bot authentication**. Determines if a request came from a legitimate source. + It's controlled by the **bot connector service** and enables secure communication between a bot and a channel. + For more information, see [Bot authentication](v4sdk/bot-builder-concept-authentication-types.md#bot-authentication). + + - **User authentication**. It enables the bot to access secured online resources on behalf of the user. + The industry standard OAuth is used to authenticate the user and authorize the bot to access the resources. + For more information, see [User authentication](v4sdk/bot-builder-concept-authentication-types.md#user-authentication). + + In summary, the Bot Framework handles the service-to-service authentication (bot authentication), essentially validating that a request + did indeed come from a proper channel. The bot is responsible for handling lower levels of authentication. + You can apply a filter so your bot only accepts requests from a particular tenant ID, or you can require your users to authenticate + with some OAuth service (user authentication). + + - question: How do I restrict the use of my bot to users belonging to my tenant only? + answer: | + You have two different options for restricting incoming messages that your bot processes. + + - If you're dealing with secure data, it's definitely recommended to use OAuth to authenticate the users. + - Using middleware is another good option. In the **Teams channel**, for example, you can create middleware to get the tenant ID from the + [Teams channel data](/microsoftteams/platform/bots/how-to/conversations/conversation-messages#teams-channel-data). + The middleware can then decide whether to let the incoming activity continue on to your bot logic or to instead return an error message. + + > [!WARNING] + > You can't prevent Teams from sending you messages from various tenants, nor can you prevent someone from installing your bot if they have your app manifest. + > All you can do is to prevent your bot from processing the undesired messages. diff --git a/articles/bot-service-resources-identifiers-guide.md b/articles/bot-service-resources-identifiers-guide.md index e95052a53..3fc8e9a84 100644 --- a/articles/bot-service-resources-identifiers-guide.md +++ b/articles/bot-service-resources-identifiers-guide.md @@ -2,24 +2,27 @@ title: Guide to IDs in the Bot Framework - Bot Service description: This guide describes the characteristics of ID fields present in the Bot Framework v3 protocol. keywords: id, bots, protocol -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 04/30/2019 - +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # ID fields in the Bot Framework +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + This guide describes the characteristics of ID fields in the Bot Framework. ## Channel ID Every Bot Framework channel is identified by a unique ID. -Example: `"channelId": "skype"` +Example: `"channelId": "slack"` Channel IDs serve as namespaces for other IDs. Runtime calls in the Bot Framework protocol must take place within the context of a channel; the channel gives meaning to the conversation and account IDs used when @@ -34,36 +37,36 @@ casing, and thus bots may use ordinal comparisons to establish equivalence. ## Bot Handle -Every bot that has been registered with the Azure Bot Service has a bot handle. +Every bot that has been registered with the Azure AI Bot Service has a bot handle. Example: `FooBot` -A bot handle represents a bot's registration with the online Azure Bot Service. This registration is associated +A bot handle represents a bot's registration with the online Azure AI Bot Service. This registration is associated with an HTTP webhook endpoint and registrations with channels. -The Azure Bot Service ensures uniqueness of bot handles. The Azure portal performs a case-insensitive +The Azure AI Bot Service ensures uniqueness of bot handles. The Azure portal performs a case-insensitive uniqueness check (meaning that case variations of bot handle are treated as a single handle) although this is a characteristic of the Azure portal, and not necessarily the bot handle itself. ### Rules for bot handles -* Bot handles are unique (case-insensitive) within the Bot Framework. +- Bot handles are unique (case-insensitive) within the Bot Framework. ## App ID -Every bot that has been registered with the Azure Bot Service has an App ID. +Every bot that has been registered with the Azure AI Bot Service has an App ID. > [!NOTE] > Previously, apps were commonly referred to as "MSA Apps" or "MSA/AAD Apps." Apps are now more commonly referred to simply as "apps", but some protocol elements may refer to apps as "MSA Apps" in perpetuity. -Example: `"msaAppId": "353826a6-4557-45f8-8d88-6aa0526b8f77"` +Example: `"msaAppId": "00001111-aaaa-2222-bbbb-3333cccc4444"` An app represents a registration with the Identity team's App portal, and serves as the service-to-service identity mechanism within the Bot Framework runtime protocol. Apps may have other non-bot associations, such as websites and mobile/desktop applications. Every registered bot has exactly one app. Although it's not possible for a bot owner to independently change which -app is associated with their bot, the Bot Framework team can do so in a small number of exceptional cases. +app is associated with their bot, the Bot Framework team can do so in a few exceptional cases. Bots and channels may use app IDs to uniquely identify a registered bot. @@ -71,9 +74,9 @@ App IDs are guaranteed to be GUIDs. App IDs should be compared without case sens ### Rules for app IDs -* App IDs are unique (GUID comparison) within the Microsoft App platform. -* Every bot has exactly one corresponding app. -* Changing which app a bot is associated with requires the assistance of the Bot Framework team. +- App IDs are unique (GUID comparison) within the Microsoft App platform. +- Every bot has exactly one corresponding app. +- Changing which app a bot is associated with requires the assistance of the Bot Framework team. ## Channel Account @@ -83,23 +86,22 @@ informative bot non-structural data, like an optional name. Example: `"from": { "id": "john.doe@contoso.com", "name": "John Doe" }` This account describes the address within the channel where messages may be sent and received. In some -cases, these registrations exist within a single service (e.g., Skype, Facebook). In others, they are registered -across many systems (email addresses, phone numbers). In more anonymous channels (e.g., Web Chat), the registration +cases, these registrations exist within a single service, such as Facebook. In others, they're registered +across many systems (email addresses, phone numbers). In more anonymous channels, such as Web Chat, the registration may be ephemeral. Channel accounts are nested within channels. A Facebook account, for example, is simply a number. This number may have a different meaning in other channels, and it doesn't have meaning outside all channels. The relationship between channel accounts and users (actual people) depends on conventions associated with -each channel. For example, an SMS number typically refers to one person for a period of time, after which +each channel. For example, an SMS number typically refers to one person, but the number may be transferred to someone else. Conversely, a Facebook account typically refers to one person -in perpetuity, although it is not uncommon for two people to share a Facebook account. +in perpetuity, although it isn't uncommon for two people to share a Facebook account. In most channels, it's appropriate to think of a channel account as a kind of mailbox where messages can be -delivered. It's typical for most channels to allow multiple address to map to a single mailbox; for example, +delivered. It's typical for most channels to allow multiple address to map to a single mailbox. For example, "jdoe@contoso.com" and "john.doe@service.contoso.com" may resolve to the same inbox. Some channels go -a step further and alter the account's address based on which bot is accessing it; for example, both Skype -and Facebook alter user IDs so every bot has a different address for sending and receiving messages. +a step further and alter the account's address based on which bot is accessing it. For example, Facebook alters user IDs so every bot has a different address for sending and receiving messages. While it's possible in some cases to establish equivalency between addresses, establishing equivalency between mailboxes and equivalency between people requires knowledge of the conventions within the channel, @@ -109,12 +111,12 @@ A bot is informed of its channel account address via the `recipient` field on ac ### Rules for channel accounts -* Channel accounts have meaning only within their associated channel. -* More than one ID may resolve to the same account. -* Ordinal comparison may be used to establish that two IDs are the same. -* There is generally no comparison that can be used to identify whether two different IDs resolve +- Channel accounts have meaning only within their associated channel. +- More than one ID may resolve to the same account. +- Ordinal comparison may be used to establish that two IDs are the same. +- There's generally no comparison that can be used to identify whether two different IDs resolve to the same account, bot or person. -* The stability of associations between IDs, accounts, mailboxes, and people depends on the channel. +- The stability of associations between IDs, accounts, mailboxes, and people depend on the channel. ## Conversation ID @@ -125,28 +127,29 @@ Example: `"conversation": { "id": "1234" }` A conversation contains an exchange of messages and other activities. Every conversation has zero or more activities, and every activity appears in exactly one conversation. Conversations may be perpetual, or may have distinct starts and ends. The process of creating, modifying, or ending a conversation occurs within -the channel (i.e., a conversation exists when the channel is aware of it) and the characteristics of these +the channel—a conversation only exists while the channel is aware of it—and the characteristics of these processes are established by the channel. The activities within a conversation are sent by users and bots. The definition for which users "participate" in a conversation varies by channel, and can theoretically include present users, users who have ever received a message, users who sent a message. -Several channels (e.g., SMS, Skype, and possibly others) have the quirk that the conversation ID assigned to a 1:1 +Several channels—such as SMS, and possibly others—have the quirk that the conversation ID assigned to a 1:1 conversation is the remote channel account ID. This quirk has two side-effects: -1. The conversation ID is subjective based on who is viewing it. If Participants A and B are talking, - participant A sees the conversation ID to be "B" and participant B sees the conversation ID to be "A." -2. If the bot has multiple channel accounts within this channel (for example, if the bot has two SMS numbers), - the conversation ID is not sufficient to uniquely identify the conversation within the bot's field of view. -Thus, a conversation ID does not necessarily uniquely identify a single conversation within a channel even +1. The conversation ID is subjective, based on who is viewing it. If Participants A and B are talking, + participant A sees the conversation ID to be "B", and participant B sees the conversation ID to be "A." +1. If the bot has multiple channel accounts within this channel (for example, if the bot has two SMS numbers), + the conversation ID isn't sufficient to uniquely identify the conversation within the bot's field of view. + +Thus, a conversation ID doesn't necessarily uniquely identify a single conversation within a channel even for a single bot. ### Rules for conversation IDs -* Conversations have meaning only within their associated channel. -* More than one ID may resolve to the same conversation. -* Ordinal equality does not necessarily establish that two conversation IDs are the same conversation, although +- Conversations have meaning only within their associated channel. +- More than one ID may resolve to the same conversation. +- Ordinal equality doesn't necessarily establish that two conversation IDs are the same conversation, although in most cases, it does. ## Activity ID @@ -156,9 +159,10 @@ Activities are sent and received within the Bot Framework protocol, and these ar Example: `"id": "5678"` Activity IDs are optional and employed by channels to give the bot a way to reference the ID in subsequent -API calls, if they are available: -* Replying to a particular activity -* Querying for the list of participants at the activity level +API calls, if they're available: + +- Replying to a particular activity +- Querying for the list of participants at the activity level Because no further use cases have been established, there are no additional rules for the treatment of activity IDs. diff --git a/articles/bot-service-resources-links-help.md b/articles/bot-service-resources-links-help.md index d777828cc..dd563e33f 100644 --- a/articles/bot-service-resources-links-help.md +++ b/articles/bot-service-resources-links-help.md @@ -1,30 +1,35 @@ --- title: Additional support resources for the Bot Framework - Bot Service -description: Learn about additional support resources for the Bot Framework. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn about resources such as the Bot Framework Samples repo, Stack Overflow, and Gitter that provide information on using the Bot Framework to develop bots. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Bot Framework additional resources +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + These resources provide additional information and support for developing bots with the Bot Framework. > [!IMPORTANT] -> Please use one of these resources for support rather than posting comments on this article. This article is not monitored -> for support requests. +> Please use one of these resources for support rather than posting comments on this article. This article isn't monitored for support requests. -| Support type | Contact | -|-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Community support | Questions can be posted at [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) using the `botframework` tag. Please note that Stack Overflow has guidelines such as requiring a descriptive title, a complete and concise problem statement, and sufficient details to reproduce your issue. Feature requests or overly broad questions are off-topic; new users should visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask) for more details. | -| Community chat group | [Gitter.IM](https://gitter.im/Microsoft/BotBuilder) | -| Using a bot | Contact the bot's developer through their publisher e-mail | -| Bot Framework SDK issues/suggestions | Use the issues tab on the GitHub repo | -| Azure help and support | Azure Help + Support | -| Documentation issues | Submit an issue to the Bot Framework documentation GitHub repo. | -| Documentation updates | Click the Edit link on an article and submit a pull request to the Bot Framework documentation GitHub repo. | -| Reporting abuse | Contact us at [bf-reports@microsoft.com](mailto://bf-reports@microsoft.com) | +| Support type | Contact | +|--|--| +| **Community support** | Questions can be posted at [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) using the `botframework` tag. Stack Overflow has guidelines such as requiring a descriptive title, a complete and concise problem statement, and sufficient details to reproduce your issue. Feature requests or overly broad questions are off-topic; new users should visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask) for more details. | +| **Community chat group** | [Gitter.IM](https://gitter.im/Microsoft/BotBuilder) | +| **Using a bot** | Contact the bot's developer through their publisher e-mail | +| **Bot Framework SDK issues/suggestions** | Submit issues and feature requests to the SDK repo for your bot's language ([C#](https://github.com/Microsoft/botbuilder-dotnet/), [JavaScript](https://github.com/Microsoft/botbuilder-js), or [Python](https://github.com/Microsoft/botbuilder-python)). How-to questions about the SDK can be posted at [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) using the `botframework` tag. | +| **Bot Framework Samples** | Submit issues with samples to the [Bot Framework Samples](https://github.com/microsoft/botbuilder-samples) repo. | +| **Azure help and support** | [Azure Help + Support](https://ms.portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade/overview) | +| **Documentation issues** | Submit an [issue](https://github.com/MicrosoftDocs/bot-framework-docs/issues) to the Bot Framework documentation GitHub repo. | +| **Documentation updates** | Click the Edit link on an article and submit a pull request to the [Bot Framework documentation GitHub repository](https://github.com/MicrosoftDocs/bot-framework-docs). | +| **Reporting abuse** | Contact us at [bf-reports@microsoft.com](mailto://bf-reports@microsoft.com) | +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] diff --git a/articles/bot-service-resources-upgrade-to-v3.md b/articles/bot-service-resources-upgrade-to-v3.md deleted file mode 100644 index c45d7fb69..000000000 --- a/articles/bot-service-resources-upgrade-to-v3.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: Upgrade your bot to Bot Framework API v3 - Bot Service -description: Learn how to upgrade your bot to Bot Framework API v3. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 ---- - -# Upgrade your bot to Bot Framework API v3 - -At Build 2016 Microsoft announced the Microsoft Bot Framework and its initial iteration of the Bot Connector API, along with Bot Builder and Bot Connector SDKs. Since then, we've been collecting your feedback and actively working to improve the REST API and SDKs. - -In July 2016, Bot Framework API v3 was released and Bot Framework API v1 was deprecated. Bots that use API v1 ceased to function on Skype in December 2016 and on all remaining channels on February 23, 2017. If you created a bot using API v1 and want to make it functional again, you must upgrade it to API v3 by following the instructions in this article. To ensure that you understand the upgrade process from end-to-end, read through this article completely before you begin. - -## Step 1: Get your App ID and password from the Bot Framework Portal - -Sign in to the [Bot Framework Portal](https://dev.botframework.com/), click **My bots**, then select your bot to open its dashboard. Next, click the **SETTINGS** link that is located left side of the page under **Bot Management**. - -Within the **Configuration** section of the settings page, examine the contents of the **Microsoft App ID** field and proceed with next steps. - - - -1. Click **Manage Microsoft App ID and password**. -![Configuration](./media/upgrade/manage-app-id.png) - -2. Click **Generate New Password**. -![Generate new password](./media/upgrade/generate-new-password.png) - -3. Copy and save the new password along with the MSA App ID; you will need these values in the future. -![New password](./media/upgrade/new-password-generated.png) - -Another method of retrieving your **Microsoft App ID and Password** can be done following these [instructions](https://blog.botframework.com/2018/07/03/find-your-azure-bots-appid-and-appsecret/). - - - -## Step 2: Update your bot code to version 4.0 - -V1 bots are no longer compatible. To update your bot you will need to create a new bot under V3 instead. If you want to preserve any of your old code you will have to migrate your code manually. - -The easiest solution is to recreate your bot with the new [SDK](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) and deploy it. - -If you wish to preserve your old code, follow the steps below: - -1. Create a new Bot application. -2. Copy over your old code to your new Bot application. -3. Upgrade the SDK to the latest version via the Nuget package manager. -4. Fix any errors that appear, reference the new [SDK](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0). -5. Deploy your bot to Azure by following these [instructions](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0) - - - -### BotBuilder and Connector are now one SDK - -Instead of having to install separate SDKs for the Builder and Connector by using multiple NuGet packages (or NPM modules), you can now get both libraries in a single Bot Framework SDK: - -- Bot Framework SDK for .NET: `Microsoft.Bot.Builder` NuGet package -- Bot Framework SDK for Node.js: `botbuilder` NPM module - -The standalone `Microsoft.Bot.Connector` SDK is now obsolete and is no longer being maintained. - -### Message is now Activity - -The `Message` object has been replaced with the `Activity` object in API v3. The most common type of activity is **message**, but there are other activity types that can be used to communicate various types of information to a bot or channel. For more information about messages, see [Create messages](~/dotnet/bot-builder-dotnet-create-messages.md) and [Send and receive activities](~/dotnet/bot-builder-dotnet-connector.md). - -### Activity types & events - -Some events have been renamed and/or refactored in API v3. In addition, a new `ActivityTypes` enumeration has been added to the Connector to eliminate the need to remember specific activity types. - -- The `conversationUpdate` Activity type replaces Bot/User Added/Removed To/From Conversation with a single method. -- The new `typing` Activity type enables your bot to indicate that it is compiling a response and to know when the user is typing a response. -- The new `contactRelationUpdate` Activity type enables your bot to know if it has been added to or removed from user's contact list. - -When your bot receives a `conversationUpdate` activity, the `MembersRemoved` property and `MembersAdded` property will indicate who was added to or removed from the conversation. When your bot receives a `contactRelationUpdate` activity, the `Action` property will indicate whether the user added the bot to or removed the bot from their contact list. For more information about activity types, see [Activities overview](~/dotnet/bot-builder-dotnet-activities.md). - -### Addressing messages - -Where the sender, recipient, and channel information is specified within a message has changed slightly in API v3: - -|API v1 field | API v3 field| -|--------|--------| -| `From` object | `From` object | -| `To` object | `Recipient` object | -| `ChannelConversationID` property | `Conversation` object| -| `ChannelId` property | `ChannelId` property | - -For more information about addressing messages, see [Send and receive activities](~/dotnet/bot-builder-dotnet-connector.md). - -### Sending replies - -In Bot Framework API v3, all replies to the user will be sent asynchronously over a separately initiated HTTP request rather than inline with the HTTP POST for the incoming message to the bot. Since no message will be returned inline to the user through the Connector, the return type of your bot's post method will be `HttpResponseMessage`. This means that your bot does not synchronously "return" the string that you wish to send to the user, but instead sends a reply message at any point in your code instead of having to reply back as a response to the incoming POST. These two methods will both send a message to a conversation: - -- `SendToConversation` -- `ReplyToConversation` - -The `SendToConversation` method will append the specified message to the end of the conversation, while the `ReplyToConversation` method will (for conversations that support it) add the specified message as a direct reply to a prior message in the conversation. For more information about these methods, see [Send and receive activities](~/dotnet/bot-builder-dotnet-connector.md). - -### Starting conversations - -In Bot Framework API v3, you can start a conversation by either using the new method `CreateDirectConversation` to start a private conversation with a single user or by using the new method `CreateConversation` to start a group conversation with multiple users. For more information about starting conversations, see [Send and receive activities](~/dotnet/bot-builder-dotnet-connector.md#start-a-conversation). - -### Attachments and options - -Bot Framework API v3 introduces a more robust implementation of attachments and cards. The `Options` type is no longer supported in API v3 and has instead been replaced by cards. For more information about adding attachments to messages using .NET, see [Add media attachments to messages](~/dotnet/bot-builder-dotnet-add-media-attachments.md) and [Add rich card attachments to messages](~/dotnet/bot-builder-dotnet-add-rich-card-attachments.md). - -### Bot data storage (bot state) - -In Bot Framework API v1, the API for managing bot state data was folded into the messaging API. In Bot Framework API v3, these APIs are separate. Now, you must use the Bot State service to get state data (instead of assuming that it will be included within the `Message` object) and to store state data (instead of passing it as part of the `Message` object). For information about managing bot state data using the Bot State service, [Manage state data](~/dotnet/bot-builder-dotnet-state.md). - -> [!IMPORTANT] -> The Bot Framework State Service API is not recommended for production environments, and may be deprecated in a future release. It is recommended that you update your bot code to use the in-memory storage for testing purposes or use one of the **Azure Extensions** for production bots. For more information, see the **Manage state data** topic for [.NET](~/dotnet/bot-builder-dotnet-state.md) or [Node](~/nodejs/bot-builder-nodejs-state.md) implementation. - -### Web.config changes - -Bot Framework API v1 stored the authentication properties with these keys in **Web.Config**: - -- `AppID` -- `AppSecret` - -Bot Framework API v3 stores the authentication properties with these keys in **Web.Config**: - -- `MicrosoftAppID` -- `MicrosoftAppPassword` - -## Step 3: Deploy your Update Bot to Azure. - -After you have upgraded your bot code to API v3 simply deploy the bot to Azure following these [instructions](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0). Since V1 is no longer supported, all bots will automatically use the V3 API when deployed to the Azure services. - - \ No newline at end of file diff --git a/articles/bot-service-resources-user-agent.md b/articles/bot-service-resources-user-agent.md index 61deb675f..684f48d08 100644 --- a/articles/bot-service-resources-user-agent.md +++ b/articles/bot-service-resources-user-agent.md @@ -1,30 +1,35 @@ --- title: Bot Framework User-Agent requests - Bot Service -description: Understanding requests from bot framework to your web server. -author: JohnD-ms -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn about requests that the Bot Framework service sends to web servers. Understand why the service sends these webhook calls. See how to stop them. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: troubleshooting +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- + # Bot Framework User-Agent requests -If you’re reading this message, you’ve probably received a request from a Microsoft Bot Framework service. This guide will help you understand the nature of these requests and provide steps to stop them, if so desired. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +If you're reading this message, you may have received a request from a Microsoft Bot Framework service. This guide will help you understand the nature of these requests and provide steps to stop them, if so desired. If you received a request from our service, it likely had a User-Agent header formatted similar to the string below: -```User-Agent: BF-DirectLine/3.0 (Microsoft-BotFramework/3.0 +https://botframework.com/ua)``` +`User-Agent: BF-DirectLine/3.0 (Microsoft-BotFramework/3.0 +https://botframework.com/ua)` The most important part of this string is the **Microsoft-BotFramework** identifier, which is used by the Microsoft Bot Framework, a collection of tools and services that allows independent software developers to create and operate their own bots. -If you’re a bot developer, you may already know why these requests are being directed to your service. If not, continue reading to learn more. +If you're a bot developer, you may already know why these requests are being directed to your service. If not, continue reading to learn more. ## Why is Microsoft contacting my service? -The Bot Framework connects users on chat services like Skype and Facebook Messenger to bots, which are web servers with REST APIs running on internet-accessible endpoints. The HTTP calls to bots (also called webhook calls) are sent only to URLs specified by a bot developer who registered with the Bot Framework developer portal. +The Bot Framework connects users on chat services like Facebook Messenger to bots, which are web servers with REST APIs running on internet-accessible endpoints. The HTTP calls to bots (also called webhook calls) are sent only to URLs specified by a bot developer who registered with the Bot Framework developer portal. -If you’re receiving unsolicited requests from Bot Framework services to your web service, it is likely because a developer has either accidentally or knowingly entered your URL as the webhook callback for their bot. +If you're receiving unsolicited requests from Bot Framework services to your web service, it's likely because a developer has either accidentally or knowingly entered your URL as the webhook callback for their bot. ## To stop these requests diff --git a/articles/bot-service-review-guidelines.md b/articles/bot-service-review-guidelines.md index f0806f993..61a1a8050 100644 --- a/articles/bot-service-review-guidelines.md +++ b/articles/bot-service-review-guidelines.md @@ -1,16 +1,25 @@ --- -ms.topic: conceptual -ms.author: kamrani -ms.service: bot-service title: Bot review guidelines +description: Learn about the minimum requirements your app integration must meet before it may be published to a Microsoft channel such as Microsoft Teams. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +ms.custom: + - evergreen --- # Bot review guidelines -We welcome you and thank you for investing your talents and time in building bot, botlets, web apps, add-ins, or skills (“app integrations”) for Microsoft channels. The following are the minimum requirements your app integration must meet before it may be published to a Microsoft channel such as Skype or Microsoft Teams. Each channel may have specific requirements in addition to the requirements detailed below. If applicable, you’ll find channel- specific terms on each channel’s configuration page, and you may be required to sign-up for a channel’s service before you can publish a bot to that channel. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + +We welcome you and thank you for investing your talents and time in building bot, botlets, web apps, add-ins, or skills ("app integrations") for Microsoft channels. The following are the minimum requirements your app integration must meet before it may be published to a Microsoft channel such as Microsoft Teams. Each channel may have specific requirements in addition to the requirements detailed below. If applicable, you'll find channel- specific terms on each channel's configuration page, and you may be required to sign-up for a channel's service before you can publish a bot to that channel. ## App Integration Policies -### 1. Value, Representation, Security and Usability. + +### 1. Value, Representation, Security and Usability Your app integration and its associated metadata must: @@ -21,36 +30,39 @@ Your app integration and its associated metadata must: - not attempt to change or extend the described functionality in violation of these policies or the applicable Microsoft channel terms; - not include or enable malware; - be testable; -- continue to run and remain responsive to user input; +- continue to run and remain responsive to user input; - include a working link to your Terms of Service; - operate as described in its description, profile, terms of use and privacy policy; - operate in accordance with the terms applicable to the Microsoft Bot Framework (or other terms applicable to its development) and the Microsoft Channel Terms (or other applicable terms for the channel upon which your app integration is published); -- inform users if your app integration includes human interaction (e.g. customer service or support with a live person); -- be localized for all languages that it supports. The text of your app integration’s description must be localized in each language that you declare. +- inform users if your app integration includes human interaction, such as customer service or support with a live person; +- be localized for all languages that it supports. The text of your app integration's description must be localized in each language that you declare. -### 2. Privacy +### 2.Privacy -- If your app integration handles users' personal information (personal information includes all information or data that identifies or could be used to identify a person, or that is associated with such information or data. Examples of personal information include: name and address, phone number, biometric identifiers, location, contacts, photos, audio & video recordings, documents, SMS, email, or other text communication, screenshots, and in some cases, combined browsing history), you must provide a prominent link from your app integration to an applicable privacy policy, and such privacy policy must comply with all applicable laws, regulations and policy requirements. This policy should cover what data you are collecting or transmitting, what you will be doing with that data, and (if applicable) who you'll be sharing it with. If you don’t have a privacy statement, here are some third-party resources* that might be of some assistance: +- If your app integration handles users' personal information (personal information includes all information or data that identifies or could be used to identify a person, or that is associated with such information or data. Examples of personal information include: name and address, phone number, biometric identifiers, location, contacts, photos, audio & video recordings, documents, SMS, email, or other text communication, screenshots, and in some cases, combined browsing history), you must provide a prominent link from your app integration to an applicable privacy policy, and such privacy policy must comply with all applicable laws, regulations and policy requirements. This policy should cover what data you're collecting or transmitting, what you will be doing with that data, and (if applicable) who you'll be sharing it with. If you don't have a privacy statement, here are some third-party resources* that might be of some assistance: Future of Privacy Forum – [Application Privacy Policy Generator](http://www.applicationprivacy.org/do-tools/privacy-policy-generator/) Iubenda – [Privacy Policy Generator](http://www.iubenda.com/en) -*_You agree to assume all risk and liability arising from your use of these third-party resources and that Microsoft is not responsible for any issues arising out of your use of them._ -- Your app integration must not collect, store or transmit personal information unrelated to its primary purpose, without first obtaining express user consent. You must obtain all consents from users to process personal information where required by law. -- You may not publish an app integration that is directed at children under the age of 13 (as defined in the Children’s Online Privacy Protection Act), without express permission from Microsoft. +_You agree to assume all risk and liability arising from your use of these third-party resources and that Microsoft is not responsible for any issues arising out of your use of them._ + +- Your app integration must not collect, store or transmit personal information unrelated to its primary purpose, without first obtaining express user consent. You must obtain all consents from users to process personal information where required by law. +- You may not publish an app integration that is directed at children under the age of 13 (as defined in the Children's Online Privacy Protection Act), without express permission from Microsoft. -### 3. Financial Transactions -- For payment enabled app integrations: +### 3. Financial Transactions + +- For payment enabled app integrations: - Your app integration may not transmit financial instrument details through the user interface; - Subject to any channel or third-party platform restrictions, your app integration may: (a) support payments through the [Microsoft Seller Center](https://seller.microsoft.com/) subject to the terms of the Microsoft Seller Center Agreement; or (b) transmit links to other secure payment services; - If your app integration enables the foregoing payment mechanisms, you must disclose this in your app integrations terms of use and privacy policy (and any profile page or website for the app integration) before the end user agrees to use your app integration; - - You must clearly indicate that an app integration is payment-enabled in the its profile and provide end users with the merchant’s customer service details; and + - You must clearly indicate that an app integration is payment-enabled in the its profile and provide end users with the merchant's customer service details; and - You may not publish app integrations on any Microsoft channel that include links or otherwise direct users to payment services for the purchase of digital goods without express permission from Microsoft. -### 4. Content +### 4. Content + - All content in your app integration and associated metadata must be either originally created by the publisher, appropriately licensed from the third-party rights holder, used as permitted by the rights holder, or used as otherwise permitted by law. -- You will not publish an app integration or content in your app integration that: +- You will not publish an app integration or content in your app integration that: - is illegal; - exploits, harms, or threatens to harm children; - includes advertising, spam, unwanted or unsolicited or bulk communications, posts or messages; @@ -59,8 +71,8 @@ Iubenda – [Privacy Policy Generator](http://www.iubenda.com/en) - is harmful to you, the Microsoft Channel or others or creates a safety risk (e.g., transmitting viruses, stalking, posting terrorist content, communicating hate speech, or advocating discrimination, hatred, or violence based on considerations of race, ethnicity, national origin, language, gender, age, disability, religion, sexual orientation, status as a veteran, or membership in any other social group); - infringes upon the rights of others (e.g., unauthorized sharing of copyrighted music or other copyrighted material; - is defamatory, libelous, slanderous, or threatening; - - violates the privacy of others; - - processes information that (a) relates to a patient’s condition, treatment or payment for treatment; (b) identifies individuals as or communicates with patients, health plan members or beneficiaries; or (c) is otherwise ‘protected health information’ under the Health Insurance Portability and Accountability Act, as amended ("HIPAA") or perform any activity governed by HIPAA if you are a ‘covered entity’ or ‘business associate’ as defined under HIPAA; + - violates the privacy of others; + - processes information that (a) relates to a patient's condition, treatment or payment for treatment; (b) identifies individuals as or communicates with patients, health plan members or beneficiaries; or (c) is otherwise 'protected health information' under the Health Insurance Portability and Accountability Act, as amended ("HIPAA") or perform any activity governed by HIPAA if you're a 'covered entity' or 'business associate' as defined under HIPAA; - is offensive in any country/region to which your app is targeted. Content may be considered offensive in certain countries/regions because of local laws or cultural norms; - - helps others to break these rules. -- You must notify Microsoft in advance by sending an email to the specific channel you have published to if you make any material changes to your app integration. Changes made to your bot’s registration may require your bot to be re-reviewed to ensure that it continues to meet the requirements stated here. Microsoft has the right, in its sole discretion, to intermittently review app integrations on any channel and remove without notice. + - helps others to break these rules. +- You must notify Microsoft in advance by sending an email to the specific channel you have published to if you make any material changes to your app integration. Changes made to your bot's registration may require your bot to be re-reviewed to ensure that it continues to meet the requirements stated here. Microsoft has the right, in its sole discretion, to intermittently review app integrations on any channel and remove without notice. diff --git a/articles/bot-service-scenario-commerce.md b/articles/bot-service-scenario-commerce.md deleted file mode 100755 index 6e4db0502..000000000 --- a/articles/bot-service-scenario-commerce.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Commerce bot scenario - Bot Service -description: Explore the Commerce bot scenario with the Bot Framework. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Commerce bot scenario - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -The [Commerce bot](bot-service-scenario-commerce.md) scenario describes a bot that replaces the traditional e-mail and phone call interactions that people typically have with a hotel's concierge service. The bot takes advantage of Cognitive Services to better process customer requests via text and voice with context gathered from integration with backend services. - -![The application bot diagram](~/media/scenarios/bot-service-scenario-commerce-bot.png) - -Here is the logic flow of a Commerce bot that functions as a concierge for a hotel: - -1. The customer uses the hotel mobile app. -2. Using Azure AD B2C, the user authenticates. -3. Using the custom Application Bot, user requests information. -4. Cognitive Services helps process the natural language request. -5. Response is reviewed by customer who can refine the question using natural conversation. -6. After the user is happy with the results, the Application Bot updates the customer’s reservation. -7. Application insights gathers runtime telemetry to help development with bot performance and usage. - -## Sample bot -The sample Commerce bot is designed around a fictitious hotel concierge service. Written in C#, customers access the Bot once they've authenticated Azure AD B2C with a hotel via the chain's member services mobile app. The chain stores reservations in a SQL Database. A customer can use natural phrase questions like "How much to rent a pool cabana for my stay". The Bot in turn has context about what hotel and the duration of the guest's stay. In addition, Language Understanding (LUIS) Service makes it easy for the bot to get context from even a simple phrase like "pool cabana". The Bot provides the answer and then can offer to book a cabana for the guest, providing choices around the number of days and type of cabana. Once the Bot has all the necessary data, it books the request. The guest can also use their voice to make the same request. - -You can download or clone the source code for this sample bot from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Components you'll use -The Commerce bot uses the following components: -- Azure AD for Authentication -- Cognitive Services: LUIS -- Application Insights - -### Azure Active Directory (Azure AD) -Azure Active Directory (Azure AD) is Microsoft’s multi-tenant cloud based directory and identity management service. As a Bot developer, Azure AD lets you focus on building your Bot by making it fast and simple to integrate with a world class identity management solution used by millions of organizations around the world. Azure AD supports a B2C connector allowing you to identify individuals using external IDs such as Google, Facebook, or a Microsoft Account. Azure AD removes the responsibility from you having to manage the user's credentials and instead focus your Bot's solution knowing you can correlate the user of the Bot with the correct data exposed by your application. - -### Cognitive Services: LUIS -As a member of the Cognitive Services family of technologies, Language Understanding (LUIS) brings the power of machine learning to your apps. Currently, LUIS supports several languages that enables your Bot to understand what a person wants. When integrating with LUIS, you express intent and define the entities your Bot understands. You then teach your Bot to understand those intents and entities by training it with example utterances. You have the ability to tweak your integration using phrase lists and regex features so that your Bot is as fluid as possible for your particular conversation needs. - -### Application Insights -Application Insights helps you get actionable insights through application performance management (APM) and instant analytics. Out of the box you get rich performance monitoring, powerful alerting, and easy-to-consume dashboards to help ensure your Bot is available and performing as you expect. You can quickly see if you have a problem, then perform a root cause analysis to find and fix the issue. - -## Next steps -Next, learn about the Cortana Skill bot scenario. - -> [!div class="nextstepaction"] -> [Cortana Skills bot scenario](bot-service-scenario-cortana-skill.md) diff --git a/articles/bot-service-scenario-cortana-skill.md b/articles/bot-service-scenario-cortana-skill.md deleted file mode 100755 index f6c1d725f..000000000 --- a/articles/bot-service-scenario-cortana-skill.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Cortana Skills bot scenario - Bot Service -description: Explore the Cortana Skills bot scenario with the Bot Framework. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Cortana Skills Bot Scenario - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -The Cortana Skills Bot extends Cortana to make it easy to book a mobile auto maintenance appointment using voice with context from your calendar. - -Cortana is your personal assistant. Using the natural interface of your voice and a custom Cortana Skill Bot, you can ask Cortana to speak to an organization, such as an auto shop, to help you make an appointment. The service can provide a list of services, times available, and duration. Cortana can look at your calendar to see if you have something at a conflicting time and if not, create the appointment and add it to your calendar. - -![The Cortana Skill bot diagram](~/media/scenarios/bot-service-scenario-cortana-skill.png) - -Here is the logic flow of a Cortana Skills bot for an auto shop: - -1. The user accesses Cortana from their PC or mobile device. -2. Using either text or voice commands, the user asks for an automobile maintenance appointment. -3. Because the bot is integrated with Cortana, it has access to the user's calendar and applies logic to the request. -4. With that information, the bot can query the auto service for valid appointments. -5. Presented with contextual options, the user can book the appointment. -6. Application insights gathers runtime telemetry to help development with bot performance and usage. - -## Sample bot -With a Cortana Skills Bot, it's all about personal context. Using Cortana you could use your voice to ask for "Bob's Mobile Maintenance" to come work on your car based on your location. Using personal information exposed via Cortana your bot can confirm the location based on where the user is at when they're talking to the bot. - -You can download or clone the source code for this sample bot from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Components you'll use -The Cortana Bot uses the following components: -- Cortana -- Application Insights - -### Cortana -Now you can add support to your Bot by creating a Cortana Skill. You use the Cortana skills kit to build new features (called skills) for Cortana. A skill is a construct that allows Cortana to do more. You build skills to integrate with your Bots allowing Cortana to complete tasks and get things done. As part of the invocation process, Cortana can (with the user's consent) pass information about the user to a skill at runtime, so that the skill can customize its experience accordingly. Cortana's contextual knowledge allows your Bot to be useful and possibly even clever for them. Once invoked, certain types of skills can manipulate Cortana's interface to have a conversation between the skill and the end user. Once published, users can see and use your skill on Cortana for Windows 10 Anniversary Update+ (Desktop and Mobile), iOS, and Android. - -### Application Insights -Application Insights helps you get actionable insights through application performance management (APM) and instant analytics. Out of the box you get rich performance monitoring, powerful alerting, and easy-to-consume dashboards to help ensure your Bot is available and performing as you expect. You can quickly see if you have a problem, then perform a root cause analysis to find and fix the issue. - -## Next steps -Next, learn about the Enterprise Productivity bot scenario. - -> [!div class="nextstepaction"] -> [Enterprise Productivity bot scenario](bot-service-scenario-enterprise-productivity.md) diff --git a/articles/bot-service-scenario-enterprise-productivity.md b/articles/bot-service-scenario-enterprise-productivity.md deleted file mode 100755 index 9b34a81ba..000000000 --- a/articles/bot-service-scenario-enterprise-productivity.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Enterprise Productivity bot scenario - Bot Service -description: Explore the Enterprise Productivity bot scenario with the Bot Framework. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Enterprise Productivity Bot Scenario - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -The Enterprise Bot shows how you can increase your productivity by integrating a bot with your Office 365 calendar and other services. - -Quickly accessing customer information without having to have a bunch of windows open is what the Enterprise Productivity Bot is all about. Using simple chat commands, a sales rep can look up a customer and check their next appointment via the Graph API and Office 365. From there they can access customer specific information stored in Dynamics CRM such as get a case or create a new one. - -![The Enterprise bot diagram](~/media/scenarios/bot-service-scenario-enterprise-bot.png) - -Here is the logic flow of an Enterprise Productivity bot: - -1. The employee accesses the Enterprise Productivity bot. -2. Azure Active Directory validates the employee's identity. -3. The Enterprise Productivity bot is able to query the employee's Office 365 calendar via the Azure Graph. -4. Using data gathered from the calendar, the bot accesses case information in Dynamics CRM. -5. The information is returned to the employee who can filter down the data without leaving the bot. -6. Application insights gathers runtime telemetry to help development with bot performance and usage. - -You can download or clone the source code for this sample bot from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Sample bot -Because Bots are accessible from a variety of channels, you could use it at your desk from a corporate portal or from Skype while on the go--you just need to be authenticated. With Azure AD integration your Enterprise Productivity Bot knows that that if you're able to access it, you've been authenticated by Azure AD. From there you can ask the bot to check when your next appointment is with a particular customer. The Bot gets this information by querying Office 365 via the Graph API. Then, if there's an appointment in the next seven days, the Bot queries CRM looking for any recent cases for the customer. The Bot responds with either no cases found or the number of open and closed cases. From there you can ask the Bot to list out the cases by type and drill into individual cases. - -## Components you'll use -The Enterprise Productivity Bot uses the following components: -- Azure AD for Authentication -- Graph API to Office 365 -- Dynamics CRM -- Application Insights - -### Azure Active Directory (Azure AD) -Azure Active Directory (Azure AD) is Microsoft’s multi-tenant cloud based directory and identity management service. As a Bot developer, Azure AD lets you focus on building your Bot by making it fast and simple to integrate with a world class identity management solution used by millions of organizations around the world. By defining an Azure AD app, you can control who has access to your Bot and the data it exposes, without implementing your own complex authentication and authorization system. - -### Graph API to Office 365 -The Microsoft Graph exposes multiple APIs from Office 365 and other Microsoft cloud services through a single endpoint at https://graph.microsoft.com. Microsoft Graph makes it easier for you and Bot to execute queries. The API exposes data from multiple Microsoft cloud services, including Exchange Online as part of Office 365, Azure Active Directory, SharePoint, and more. You can use the API to navigate between entities and relationships. You can use the API from your Bots using the SDK or REST endpoints as well as from your other apps with native support Android, iOS, Ruby, UWP, Xamarin and more. - -### Dynamics CRM -Dynamics CRM is a customer engagement platform. Using Bots and CRM's APIs, you can build rich interactive Bots that can access the rich data stored in CRM. The power of Dynamics CRM is available to your Bot to create cases, check on status, knowledge management searches and more. - -### Application Insights -Application Insights helps you get actionable insights through application performance management (APM) and instant analytics. Out of the box you get rich performance monitoring, powerful alerting, and easy-to-consume dashboards to help ensure your Bot is available and performing as you expect. You can quickly see if you have a problem, then perform a root cause analysis to find and fix the issue. - -## Next steps -Next, learn about the Information bot scenario. - -> [!div class="nextstepaction"] -> [Information bot scenario](bot-service-scenario-informational.md) diff --git a/articles/bot-service-scenario-informational.md b/articles/bot-service-scenario-informational.md deleted file mode 100755 index c4eb93154..000000000 --- a/articles/bot-service-scenario-informational.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Information bot scenario - Bot Service -description: Explore the Information bot scenario with the Bot Framework. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Information Bot Scenario - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -This Information Bot could answer questions defined in a knowledge set or FAQ using Cognitive Services QnA Maker and answer more open-ended questions by using Azure Search. - -Often information is buried in structured data stores like SQL Server that can be easily surfaced via search. Imagine looking up a customer's order status by simple conversational commands. Using Cognitive Services QnA Maker, the user is presented with a set of valid search options like, lookup a customer, review a customer's most recent order, etc. With the QnA format defined the user can easily ask questions that are backed by Azure Search which can look up data stored in a SQL Database. - -![The Information bot diagram](~/media/scenarios/bot-service-scenario-informational-bot.png) - -Here is the logic flow of an Information bot: - -1. The employee starts the Information bot. -2. Azure Active Directory validates the employee's identity. -3. The employee can ask the bot what type of queries are supported. -4. Cognitive Services returns a FAQ bot built with the QnA Maker. -5. The employee defines a valid query. -6. The bot submits the query to Azure Search which returns information about the application data. -7. Application insights gathers runtime telemetry to help development with bot performance and usage. - -## Sample bot -The sample Bot, written in C#, runs in Microsoft Azure working with data indexed by Azure Search from a SQL Database instance. The Bot exposes a list of questions that can be asked with information on how to phrase the question (the answer) using Cognitive Services: QnA Maker. The user of the Bot can then type a query that looks up data via Azure Search in a broad or specific area of the database that is indexed. The sample provides a simple database with customers and order information. Application Insights tracks Bot usage and helps you monitor the Bot for exceptions. The Bot is published under as an Azure AD app so that you can restrict who has access to the information. - -You can download or clone the source code for this sample bot from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Components you'll use -The Information Bot uses the following components: -- Azure AD for Authentication -- Cognitive Services: QnA Maker -- Azure Search -- Application Insights - -### Azure Active Directory (Azure AD) -Azure Active Directory (Azure AD) is Microsoft’s multi-tenant cloud based directory and identity management service. As a Bot developer, Azure AD lets you focus on building your Bot by making it fast and simple to integrate with a world class identity management solution used by millions of organizations around the world. By defining an Azure AD app, you can control who has access to your Bot and the data it exposes, without implementing your own complex authentication and authorization system. - -### Cognitive Services: QnA Maker -Cognitive Services QnA Maker helps you provide an FAQ data source which your users can query from your Bot. When approaching vast amounts of information stored in different systems, it can be useful to help users filter down the information source and set. A single SQL database can have enormous amounts of information that when a free form search is applied brings back too much information. By first using QnA Maker, you can define a road map for your Bot users so they know how to ask intelligent questions that can then be retrieved via Azure Search. - -### Azure Search -Azure Search is a cloud search service for apps that let you get your search indices up and running quickly. Running on top of Microsoft Azure, you can easily scale up and down as your usage demands. You can connect search results to business goals with great control over search ranking and surface data hidden in your databases. - -### Application Insights -Application Insights helps you get actionable insights through application performance management (APM) and instant analytics. Out of the box you get rich performance monitoring, powerful alerting, and easy-to-consume dashboards to help ensure your Bot is available and performing as you expect. You can quickly see if you have a problem, then perform a root cause analysis to find and fix the issue. - -## Next steps -Next, learn about the Internet of Things bot scenario. - -> [!div class="nextstepaction"] -> [Internet of Things bot scenario](bot-service-scenario-internet-things.md) diff --git a/articles/bot-service-scenario-internet-things.md b/articles/bot-service-scenario-internet-things.md deleted file mode 100755 index 42bcea00d..000000000 --- a/articles/bot-service-scenario-internet-things.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Internet of Things bot scenario - Bot Service -description: Explore the Internet of Things bot scenario with the Bot Framework. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Internet of Things (IoT) Bot Scenario - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -This Internet of Things (IoT) Bot makes it easy for you to control devices around your home, such as a Philips Hue light using voice or interactive chat commands. - -People love to talk to their things. Since the days of the first TV remote, people have loved not having to move to affect their environment. This IoT bot allows a person to manage a Philips Hue by simple chat commands or voice. In addition, when using chat, a person can be given visual choices related to colors to pick. - -![The Internet of Things bot diagram](~/media/scenarios/bot-service-scenario-iot-bot.png) - -Here is the logic flow of an IoT bot: - -1. The user logs into Skype and accesses the IoT bot. -2. Using voice, the user asks the bot to turn on the lights via the IoT device. -3. The request is relayed to a 3rd party service that has access to the IoT device network. -4. The results of the command are returned to the user. -5. Application insights gathers runtime telemetry to help development with bot performance and usage. - -## Sample bot -The IoT bot will allow you to quickly use chat commands from channels like Skype or Slack to control your Hue. To facilitate remote access, you'll call IFTTT applets predefined to work with Hue. - -You can download or clone the source code for this sample bot from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Components you'll use -The Internet of Things (IoT) Bot uses the following components: -- Philips Hue -- If This Then That (IFTTT) -- Application Insights - -### Philips Hue -Philips Hue connected bulbs and bridge let you to take full control of your lighting. Whatever you want to do with your lighting, Hue can. Hue has an API you can use from your local network. However, you want to be able to access your Hue controlled devices and lights from anywhere using a friendly Bot interface. Thus you'll access Hue via IFTTT. - -### IFTTT -IFTTT is a free web-based service that people use to create chains of simple conditional statements, called applets. You can trigger an applet from your Bot to have it do something on your behalf. There are a number of predefined Hue applets available to turn lights on and off, change the scene, and more. - -### Application Insights -Application Insights helps you get actionable insights through application performance management (APM) and instant analytics. Out of the box you get rich performance monitoring, powerful alerting, and easy-to-consume dashboards to help ensure your Bot is available and performing as you expect. You can quickly see if you have a problem, then perform a root cause analysis to find and fix the issue. diff --git a/articles/bot-service-scenario-overview.md b/articles/bot-service-scenario-overview.md deleted file mode 100755 index 4af32f336..000000000 --- a/articles/bot-service-scenario-overview.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Bot Service scenarios overview - Bot Service -description: Explore key scenarios for powerful and successful bots built with Bot Service. -author: BrianRandell -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Bot scenarios - -[!INCLUDE [pre-release-label](includes/pre-release-label-v3.md)] - -This topic explores key scenarios for powerful and successful bots built using Bot Service. - -You can download or clone the source code for all the scenarios bot samples from [Samples for Common Bot Framework Scenarios](https://aka.ms/abs-scenarios). - -## Commerce bot scenario -The [Commerce bot](bot-service-scenario-commerce.md) scenario describes a bot that replaces the traditional e-mail and phone call interactions that people typically have with a hotel's concierge service. The bot takes advantage of Cognitive Services to better process customer requests via text and voice with context gathered from integration with backend services. - -In the Commerce bot scenario, a customer can make a request for concierge services with a hotel. She is authenticated via an Azure Active Directory v2 authentication endpoint. The bot can look up the customer's reservations and provide different service options. For example, the customer might book a cabana by the pool. The bot uses Language Understanding Intelligent Services (LUIS) to parse the request and then the bot walks the user through the process of booking a cabana for an existing reservation. - -## Cortana Skill bot scenario -The [Cortana Skill bot](bot-service-scenario-cortana-skill.md) scenario takes advantage of Cortana. Using the natural interface of your voice and a custom Cortana Skill bot, you can ask Cortana to speak to a organization, such as a mobile auto detailing company, to help you make an appointment based on where you’re at when you make the call. The bot can provide a list of services, times available, and duration. - -## Enterprise Productivity bot scenario -The [Enterprise Productivity bot](bot-service-scenario-enterprise-productivity.md) scenario shows you how to integrate a bot with your Office 365 calendar and other services to increase your productivity. - -The bot integrates with Office 365 to make it quicker and easier to create a meeting request with another person. In the process of doing so you could access additional services like Dynamics CRM. This sample provides the code necessary to integrate with Office 365 with authentication via Azure Active Directory. It provides mock entry points for external services as an exercise for the reader. - -## Information bot scenario -This [Information bot](bot-service-scenario-informational.md) can answer questions defined in a knowledge set or FAQ using Cognitive Services QnA Maker and answer more open-ended questions Azure Search. - -Often information is buried in structured data stores like SQL Server that can be easily surfaced via search. Imagine looking up a customer's order status by simple conversational commands. Using Cognitive Services QnA Maker, the user is presented with a set of valid search options like, lookup a customer, review customer's most recent order, etc. With the QnA format defined the user can easily ask questions that are backed by Azure Search which can look up data stored in a SQL Database. - -## IoT bot scenario -This [Internet of Things (IoT) bot](bot-service-scenario-internet-things.md) bot makes it easy for you to control devices around your home, such as a Philips Hue light using interactive chat commands. - -Using this simple bot, you can control your Philips Hue lights in conjunction with the free If This Then That (IFTTT) service. As an IoT device, the Philips Hue can be controlled locally via their exposed API. However, this API is not exposed for general access from outside the local network. However, IFTTT is a "[Friend of Hue](http://www2.meethue.com/friends-of-hue/ifttt/)" and thus has exposed a number of control commands that you can issue such as turning lights on and off, changing the light color, or the light intensity. - -## Next steps -Now that you have an overview of the scenarios, dive deeper into each scenario. - -> [!div class="nextstepaction"] -> [Commerce bot](bot-service-scenario-commerce.md) diff --git a/articles/bot-service-troubleshoot-500-errors.md b/articles/bot-service-troubleshoot-500-errors.md index 56177395b..5491a9f7e 100644 --- a/articles/bot-service-troubleshoot-500-errors.md +++ b/articles/bot-service-troubleshoot-500-errors.md @@ -1,31 +1,40 @@ --- -title: Troubleshoot bot HTTP 500 errors - Bot Service -description: How to troubleshoot HTTP 500 errors in a deployed bot. +title: Troubleshoot HTTP 500 Internal Service Errors with Azure AI Bot Service +description: Learn how to troubleshoot HTTP 500 errors. See how to enable Application Insights, retrieve information on exceptions, and check logs and configuration files. keywords: troubleshoot, HTTP 500, problems. -author: jonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/19/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: troubleshooting +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Troubleshoot HTTP 500 errors +# Troubleshoot HTTP 500 Internal Service Errors + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + + The first step in troubleshooting 500 errors is enabling Application Insights. - -See [conversation analytics telemetry](https://aka.ms/botframeworkanalytics) for information about how to add Application Insights to an existing bot. +For AppInsights samples, see the luis-with-appinsights [C# sample](https://github.com/Microsoft/BotBuilder-Samples/blob/main/samples/csharp_dotnetcore/21.corebot-app-insights/) and [JS sample](https://github.com/Microsoft/BotBuilder-Samples/blob/main/samples/javascript_nodejs/21.corebot-app-insights). + +[!INCLUDE [luis-sunset-alert](includes/luis-sunset-alert.md)] -## Enable Application Insights on ASP.Net +See [conversation analytics telemetry](./v4sdk/bot-builder-telemetry.md) for information about how to add Application Insights to an existing bot. -For basic Application Insights support, see how to [set up Application Insights for your ASP.NET website](https://docs.microsoft.com/azure/application-insights/app-insights-asp-net). The Bot Framework (starting with v4.2) provides an additional level of Application Insights telemetry, but it is not required for diagnosing HTTP 500 errors. +## Enable Application Insights for ASP.NET -## Enable Application Insights on Node.js +For basic Application Insights support, see how to [set up Application Insights for your ASP.NET website](/azure/application-insights/app-insights-asp-net). The Bot Framework (starting with v4.2) provides an additional level of Application Insights telemetry, but it's not required for diagnosing HTTP 500 errors. -For basic Application Insights support, see how to [monitor your Node.js services and apps with Application Insights](https://docs.microsoft.com/azure/azure-monitor/learn/nodejs-quick-start). The Bot Framework (starting with v4.2) provides an additional level of Application Insights telemetry, but it is not required for diagnosing HTTP 500 errors. +## Enable Application Insights for Node.js + +For basic Application Insights support, see how to [monitor your Node.js services and apps with Application Insights](/azure/azure-monitor/learn/nodejs-quick-start). The Bot Framework (starting with v4.2) provides an additional level of Application Insights telemetry, but it's not required for diagnosing HTTP 500 errors. ## Query for exceptions @@ -56,15 +65,15 @@ union_all | order by timestamp desc ``` -If you have only `exceptions`, analyze the details and see if they correspond to lines in your code. If you only see exceptions coming from the Channel Connector (`Microsoft.Bot.ChannelConnector`) then see [No Application Insights events](#no-application-insights-events) to ensure that Application Insights is set up correctly and your code is logging events. +If you only have `exceptions`, analyze the details and see if they correspond to lines in your code. If you only see exceptions coming from the Channel Connector (`Microsoft.Bot.ChannelConnector`), then see [No Application Insights events](#no-application-insights-events) to ensure that Application Insights is set up correctly and your code is logging events. ## No Application Insights events -If you are receiving 500 errors and there are no further events within Application Insights from your bot, check the following: +If you're receiving 500 errors and there are no further events within Application Insights from your bot, check the following: -### Ensure bot runs locally +### Ensure the bot runs locally -Make sure your bot runs locally first with the emulator. +First, test your bot locally with the Bot Framework Emulator. ### Ensure configuration files are being copied (.NET only) @@ -142,16 +151,16 @@ Ensure there's an Application Insights key included in your config file. ### Check logs -Bot ASP.Net and Node will emit logs at the server level that can be inspected. +Bot ASP.NET and Node will emit logs at the server level that can be inspected. #### Set up a browser to watch your logs -1. Open your bot in the [Azure Portal](https://portal.azure.com/). +1. Open your bot in the [Azure portal](https://portal.azure.com/). 1. Open the **App Service Settings / All App service settings** page to see all service settings. 1. Open the **Monitoring / Diagnostics Logs** page for the app service. - Ensure that **Application Logging (Filesystem)** is enabled. Be sure to click **Save** if you change this setting. 1. Switch to the **Monitoring / Log Stream** page. - - Select **Web server logs** and ensure you see a message that you are connected. It should look something like the following: + - Select **Web server logs** and ensure you see a message that you're connected. It should look something like the following: ```bash Connecting... @@ -162,11 +171,15 @@ Bot ASP.Net and Node will emit logs at the server level that can be inspected. #### Set up browser to restart your bot service -1. Using a separate browser, open your bot in the Azure Portal. +1. Using a separate browser, open your bot in the Azure portal. 1. Open the **App Service Settings / All App service settings** page to see all service settings. 1. Switch to the **Overview** page for the app service and click **Restart**. - - It will prompt if you are sure; select **yes**. + - It will prompt if you're sure; select **yes**. 1. Return to the first browser window and watch the logs. -1. Verify that you are receiving new logs. - - If there is no activity, redeploy your bot. +1. Verify that you're receiving new logs. + - If there's no activity, redeploy your bot. - Then switch to the **Application logs** page and look for any errors. + +## Next steps + +- [General troubleshooting for Azure AI Bot Service bots](./bot-service-troubleshoot-general-problems.md) diff --git a/articles/bot-service-troubleshoot-authentication-problems.md b/articles/bot-service-troubleshoot-authentication-problems.md index 96640d4ad..5bbbf5144 100644 --- a/articles/bot-service-troubleshoot-authentication-problems.md +++ b/articles/bot-service-troubleshoot-authentication-problems.md @@ -1,73 +1,62 @@ --- title: Troubleshooting Bot Framework Authentication - Bot Service -description: Learn how to troubleshoot authentication errors with your bot. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 04/30/2019 +description: Learn how to troubleshoot bot authentication errors, such as connectivity issues and problems with app IDs and passwords. View additional resources. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: troubleshooting +ms.custom: + - evergreen --- # Troubleshooting Bot Framework authentication -This guide can help you to troubleshoot authentication issues with your bot by evaluating a series of scenarios to determine where the problem exists. +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + + + +This guide can help you to troubleshoot authentication issues with your bot by evaluating a series of scenarios to determine where the problem exists. > [!NOTE] -> To complete all steps in this guide, you will need to download and use the [Bot Framework Emulator][Emulator] and must have access to the bot's registration settings in the Azure Portal. +> To complete all steps in this guide, you'll need to download and use the [Bot Framework Emulator][Emulator] and must have access to the bot's registration settings in the [Azure portal](https://portal.azure.com). -## App ID and password +## App ID and password -Bot security is configured by the **Microsoft App ID** and **Microsoft App Password** that you obtain when you register your bot with the Bot Framework. These values are typically specified within the bot's configuration file and used to retrieve access tokens from the Microsoft Account service. +Bot security is configured by the **Microsoft App ID** and **Microsoft App Password** that you obtain when you register your bot with the Bot Framework. These values are typically specified within the bot's configuration file and used to retrieve access tokens from the Microsoft Account service. -If you have not yet done so, [deploy your bot to azure](~/bot-builder-howto-deploy-azure.md) to obtain a **Microsoft App ID** and **Microsoft App Password** that it can use for authentication. +If you haven't yet done so, [deploy your bot to Azure](bot-builder-howto-deploy-azure.md) to obtain a **Microsoft App ID** and **Microsoft App Password** that it can use for authentication. > [!NOTE] -> To find your bot's **AppID** and **AppPassword** for an already deployed bot, see [MicrosoftAppID and MicrosoftAppPassword](bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). +> To find your bot's **AppID** and **AppPassword** for an already deployed bot, see [MicrosoftAppID and MicrosoftAppPassword](bot-service-manage-overview.md#bot-identity-information). ## Step 1: Disable security and test on localhost -In this step, you will verify that your bot is accessible and functional on localhost when security is disabled. +In this step, you'll verify that your bot is accessible and functional on localhost when security is disabled. > [!WARNING] -> Disabling security for your bot may allow unknown attackers to impersonate users. Only implement the following procedure if you are operating in a protected debugging environment. - -### Disable security - -To disable security for your bot, edit its configuration settings to remove the values for app ID and password. - -::: moniker range="azure-bot-service-3.0" - -If you're using the Bot Framework SDK for .NET, edit these settings in your Web.config file: +> Disabling security for your bot may allow unknown attackers to impersonate users. Only implement the following procedure if you're operating in a protected debugging environment. -```xml - - - - -``` - -If you're using the Bot Framework SDK for Node.js, edit these values (or update the corresponding environment variables): + -```javascript -var connector = new builder.ChatConnector({ - appId: null, - appPassword: null -}); -``` +### Disable security -::: moniker-end +To disable security for your bot, edit its configuration settings to remove the values for app ID and password. -::: moniker range="azure-bot-service-4.0" +### [C#](#tab/csharp) -If you're using the Bot Framework SDK for .NET, edit the settings in your `appsettings.json` file: +If you're using the Bot Framework SDK for .NET, add or edit the settings in your **appsettings.json** file: ```json - "MicrosoftAppId": "", - "MicrosoftAppPassword": "" +"MicrosoftAppId": "", +"MicrosoftAppPassword": "" ``` -If you're using the Bot Framework SDK for Node.js, edit these values (or update the corresponding environment variables): +### [JavaScript](#tab/javascript) + +If you're using the Bot Framework SDK for Node.js, add or edit the following values in your **.env** file: ```javascript const adapter = new BotFrameworkAdapter({ @@ -76,39 +65,52 @@ const adapter = new BotFrameworkAdapter({ }); ``` -::: moniker-end +### [Python](#tab/python) + +If you're using the Bot Framework SDK for Python, add or edit the following values in your **config.py** file: + +```python +class DefaultConfig: + """ Bot Configuration """ + APP_ID = None + APP_PASSWORD = None +``` + +--- -### Test your bot on localhost +### Test your bot on localhost Next, test your bot on localhost by using the Bot Framework Emulator. 1. Start your bot on localhost. -2. Start the Bot Framework Emulator. -3. Connect to your bot using the emulator. - - Type `http://localhost:port-number/api/messages` into the emulator's address bar, where **port-number** matches the port number shown in the browser where your application is running. +1. Start the Bot Framework Emulator. +1. Connect to your bot using the Emulator. + - Type `http://localhost:port-number/api/messages` into the Emulator's address bar, where **port-number** matches the port number shown in the browser where your application is running. - Ensure that the **Microsoft App ID** and **Microsoft App Password** fields are both empty. - Click **Connect**. -4. To test connectivity to your bot, type some text into the emulator and press Enter. +1. To test connectivity to your bot, type some text into the Emulator and press Enter. -If the bot responds to the input and there are no errors in the chat window, you have verified that your bot is accessible and functional on localhost when security is disabled. Proceed to [Step 2](#step-2). +If the bot responds to the input and there are no errors in the chat window, you've verified that your bot is accessible and functional on localhost when security is disabled. Proceed to [Step 2](#step-2). If one or more error(s) are indicated in the chat window, click the error(s) for details. Common issues include: -* The emulator settings specify an incorrect endpoint for the bot. Make sure you have included the proper port number in the URL and the proper path at the end of the URL (e.g., `/api/messages`). -* The emulator settings specify a bot endpoint that begins with `https`. On localhost, the endpoint should begin with `http`. -* The emulator settings specify a value for the **Microsoft App ID** field and/or the **Microsoft App Password** field. Both fields should be empty. -* Security has not been disabled of for the bot. [Verify](#disable-security-localhost) that the bot does not specify a value for either app ID or password. +- The Emulator settings specify an incorrect endpoint for the bot. Make sure you've included the proper port number in the URL and the proper path at the end of the URL, such as `/api/messages`. +- The Emulator settings specify a bot endpoint that begins with `https`. On localhost, the endpoint should begin with `http`. +- The Emulator settings specify a value for the **Microsoft App ID** field and/or the **Microsoft App Password** field. Both fields should be empty. +- Security hasn't been disabled of for the bot. [Verify](#disable-security-localhost) that the bot doesn't specify a value for either app ID or password. -## Step 2: Verify your bot's app ID and password + -In this step, you will verify that the app ID and password that your bot will use for authentication are valid. (If you do not know these values, [obtain them](#PW) now.) +## Step 2: Verify your bot's app ID and password + +In this step, you'll verify that the app ID and password that your bot will use for authentication are valid. (If you don't know these values, [obtain them](#app-id-and-password) now.) > [!WARNING] > The following instructions disable SSL verification for `login.microsoftonline.com`. Only perform this procedure on a secure network and consider changing your application's password afterward. ### Issue an HTTP request to the Microsoft login service -These instructions describe how to use [cURL](https://curl.haxx.se/download.html) to issue an HTTP request to the Microsoft login service. You may use an alternative tool such as Postman, just ensure that the request conforms to the Bot Framework [authentication protocol](~/rest-api/bot-framework-rest-connector-authentication.md). +These instructions describe how to use [cURL](https://curl.haxx.se/download.html) to issue an HTTP request to the Microsoft login service. You may use an alternative tool such as Postman, just ensure that the request conforms to the Bot Framework [authentication protocol](rest-api/bot-framework-rest-connector-authentication.md). To verify that your bot's app ID and password are valid, issue the following request using **cURL**, replacing `APP_ID` and `APP_PASSWORD` with your bot's app ID and password. @@ -119,21 +121,25 @@ To verify that your bot's app ID and password are valid, issue the following req curl -k -X POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token -d "grant_type=client_credentials&client_id=APP_ID&client_secret=APP_PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default" ``` -This request attempts to exchange your bot's app ID and password for an access token. If the request is successful, you will receive a JSON payload that contains an `access_token` property, amongst others. +This request attempts to exchange your bot's app ID and password for an access token. If the request is successful, you'll receive a JSON payload that contains an `access_token` property, amongst others. ```json {"token_type":"Bearer","expires_in":3599,"ext_expires_in":0,"access_token":"eyJ0eXAJKV1Q..."} ``` -If the request is successful, you have verified that the app ID and password that you specified in the request are valid. Proceed to [Step 3](#step-3). +If the request is successful, you've verified that the app ID and password that you specified in the request are valid. Proceed to [Step 3](#step-3). + +If you receive an error in response to the request, examine the response to identify the cause of the error. If the response indicates that the app ID or the password is invalid, [obtain the correct values](#app-id-and-password) from the Bot Framework Portal and reissue the request with the new values to confirm that they're valid. + + -If you receive an error in response to the request, examine the response to identify the cause of the error. If the response indicates that the app ID or the password is invalid, [obtain the correct values](#PW) from the Bot Framework Portal and re-issue the request with the new values to confirm that they are valid. +## Step 3: Enable security and test on localhost -## Step 3: Enable security and test on localhost +At this point, you've verified that your bot is accessible and functional on localhost when security is disabled and confirmed that the app ID and password that the bot will use for authentication are valid. In this step, you'll verify that your bot is accessible and functional on localhost when security is enabled. -At this point, you have verified that your bot is accessible and functional on localhost when security is disabled and confirmed that the app ID and password that the bot will use for authentication are valid. In this step, you will verify that your bot is accessible and functional on localhost when security is enabled. + -### Enable security +### Enable security Your bot's security relies on Microsoft services, even when your bot is running only on localhost. To enable security for your bot, edit its configuration settings to populate app ID and password with the values that you verified in [Step 2](#step-2). Additionally, make sure your packages are up to date, specifically `System.IdentityModel.Tokens.Jwt` and `Microsoft.IdentityModel.Tokens`. @@ -156,72 +162,71 @@ var connector = new builder.ChatConnector({ ``` > [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). +> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](bot-service-manage-overview.md#bot-identity-information). -### Test your bot on localhost +### Test your bot on localhost Next, test your bot on localhost by using the Bot Framework Emulator. 1. Start your bot on localhost. -2. Start the Bot Framework Emulator. -3. Connect to your bot using the emulator. - - Type `http://localhost:port-number/api/messages` into the emulator's address bar, where **port-number** matches the port number shown in the browser where your application is running. +1. Start the Bot Framework Emulator. +1. Connect to your bot using the Emulator. + - Type `http://localhost:port-number/api/messages` into the Emulator's address bar, where **port-number** matches the port number shown in the browser where your application is running. - Enter your bot's app ID into the **Microsoft App ID** field. - Enter your bot's password into the **Microsoft App Password** field. - Click **Connect**. -4. To test connectivity to your bot, type some text into the emulator and press Enter. +1. To test connectivity to your bot, type some text into the Emulator and press Enter. -If the bot responds to the input and there are no errors in the chat window, you have verified that your bot is accessible and functional on localhost when security is enabled. Proceed to [Step 4](#step-4). +If the bot responds to the input and there are no errors in the chat window, you've verified that your bot is accessible and functional on localhost when security is enabled. Proceed to [Step 4](#step-4-test-your-bot-in-the-cloud). If one or more error(s) are indicated in the chat window, click the error(s) for details. Common issues include: -* The emulator settings specify an incorrect endpoint for the bot. Make sure you have included the proper port number in the URL and the proper path at the end of the URL (e.g., `/api/messages`). -* The emulator settings specify a bot endpoint that begins with `https`. On localhost, the endpoint should begin with `http`. -* In the emulator settings, the **Microsoft App ID** field and/or the **Microsoft App Password** do not contain valid values. Both fields should be populated and each field should contain the corresponding value that you verified in [Step 2](#step-2). -* Security has not been enabled for the bot. [Verify](#enable-security-localhost) that the bot configuration settings specify values for both app ID and password. +- The Emulator settings specify an incorrect endpoint for the bot. Make sure you've included the proper port number in the URL and the proper path at the end of the URL, such as `/api/messages`. +- The Emulator settings specify a bot endpoint that begins with `https`. On localhost, the endpoint should begin with `http`. +- In the Emulator settings, the **Microsoft App ID** field and/or the **Microsoft App Password** don't contain valid values. Both fields should be populated and each field should contain the corresponding value that you verified in [Step 2](#step-2). +- Security hasn't been enabled for the bot. [Verify](#enable-security-localhost) that the bot configuration settings specify values for both app ID and password. -## Step 4: Test your bot in the cloud +## Step 4: Test your bot in the cloud -At this point, you have verified that your bot is accessible and functional on localhost when security is disabled, confirmed that your bot's app ID and password are valid, and verified that your bot is accessible and functional on localhost when security is enabled. In this step, you will deploy your bot to the cloud and verify that it is accessible and functional there with security enabled. +At this point, you've verified that your bot is accessible and functional on localhost when security is disabled, confirmed that your bot's app ID and password are valid, and verified that your bot is accessible and functional on localhost when security is enabled. In this step, you'll deploy your bot to the cloud and verify that it's accessible and functional there with security enabled. ### Deploy your bot to the cloud The Bot Framework requires that bots be accessible from the internet, so you must deploy your bot to a cloud hosting platform such as Azure. Be sure to enable security for your bot prior to deployment, as described in [Step 3](#step-3). > [!NOTE] -> If you do not already have a cloud hosting provider, you can register for a free account.. +> If you don't already have a cloud hosting provider, you can register for a [free account](https://azure.microsoft.com/free/). If you deploy your bot to Azure, SSL will automatically be configured for your application, thereby enabling the **HTTPS** endpoint that the Bot Framework requires. If you deploy to another cloud hosting provider, be sure to verify that your application is configured for SSL so that the bot will have an **HTTPS** endpoint. -### Test your bot +### Test your bot To test your bot in the cloud with security enabled, complete the following steps. -1. Ensure that your bot has been successfully deployed and is running. -2. Sign in to the Azure Portal. -3. Click **My Bots**. -4. Select the bot that you want to test. -5. Click **Test** to open the bot in an embedded web chat control. -6. To test connectivity to your bot, type some text into the web chat control and press Enter. +1. Ensure that your bot has been successfully deployed and is running. +1. Sign in to the [Azure portal](https://portal.azure.com). +1. Navigate to the **Azure Bot** resource for your bot within the portal. +1. Click **Test in Web Chat** in the **Bot management** pane on the left. +1. To test connectivity to your bot, type some text into the web chat control and press Enter. -If an error is indicated in the chat window, use the error message to determine the cause of the error. Common issues include: +If an error is indicated in the chat window, use the error message to determine the cause of the error. Common issues include: -* The **Messaging endpoint** specified on the **Settings** page for your bot in the Bot Framework Portal is incorrect. Make sure you have included the proper path at the end of the URL (e.g., `/api/messages`). -* The **Messaging endpoint** specified on the **Settings** page for your bot in the Bot Framework Portal does not begin with `https` or is not trusted by the Bot Framework. Your bot must have a valid, chain-trusted certificate. -* The bot is configured with missing or incorrect values for app ID or password. [Verify](#enable-security-localhost) that the bot configuration settings specify valid values for app ID and password. +- The **Messaging endpoint** specified on the **Settings** page for your bot in the Bot Framework Portal is incorrect. Make sure you've included the proper path at the end of the URL, such as `/api/messages`. +- The **Messaging endpoint** specified on the **Settings** page for your bot in the Bot Framework Portal doesn't begin with `https` or isn't trusted by the Bot Framework. Your bot must have a valid, chain-trusted certificate. +- The bot is configured with missing or incorrect values for app ID or password. [Verify](#enable-security-localhost) that the bot configuration settings specify valid values for app ID and password. -If the bot responds appropriately to the input, you have verified that your bot is accessible and functional in the cloud with security enabled. At this point, your bot is ready to securely [connect to a channel](~/bot-service-manage-channels.md) such as Skype, Facebook Messenger, Direct Line, and others. +If the bot responds appropriately to the input, you've verified that your bot is accessible and functional in the cloud with security enabled. At this point, your bot is ready to securely [connect to a channel](bot-service-manage-channels.md) such as Facebook Messenger, Direct Line, and others. ## Additional resources -If you are still experiencing issues after completing the steps above, you can: +If you're still experiencing issues after completing the steps above, you can: -* Review how-to [debug a bot](bot-service-debug-bot.md) and the other debugging articles in that section. -* [Debug your bot in the cloud](~/bot-service-debug-emulator.md) using the Bot Framework Emulator and ngrok tunnelling software. *ngrok is not a Microsoft product.* -* Use a proxying tool like [Fiddler](https://www.telerik.com/fiddler) to inspect HTTPS traffic to and from your bot. *Fiddler is not a Microsoft product.* -* Review the [Bot Connector authentication guide][BotConnectorAuthGuide] to learn about the authentication technologies that the Bot Framework uses. -* Solicit help from others by using the Bot Framework [support][Support] resources. +- Review how-to [debug a bot](bot-service-debug-bot.md) and the other debugging articles in that section. +- [Debug your bot in the cloud](bot-service-debug-emulator.md) using the Bot Framework Emulator and [Dev Tunnels](https://aka.ms/devtunnels tunneling software. +- Use a proxying tool like [Fiddler](https://www.telerik.com/fiddler) to inspect HTTPS traffic to and from your bot. *Fiddler isn't a Microsoft product.* +- Review the [Bot Connector authentication guide][BotConnectorAuthGuide] to learn about the authentication technologies that the Bot Framework uses. +- Solicit help from others by using the Bot Framework [support][Support] resources. -[BotConnectorAuthGuide]: ~/rest-api/bot-framework-rest-connector-authentication.md +[BotConnectorAuthGuide]: ./rest-api/bot-framework-rest-connector-authentication.md [Support]: bot-service-resources-links-help.md [Emulator]: bot-service-debug-emulator.md diff --git a/articles/bot-service-troubleshoot-bot-configuration.md b/articles/bot-service-troubleshoot-bot-configuration.md index 5f6357aad..d73341b8c 100644 --- a/articles/bot-service-troubleshoot-bot-configuration.md +++ b/articles/bot-service-troubleshoot-bot-configuration.md @@ -1,39 +1,51 @@ --- title: Troubleshoot bot configuration issues - Bot Service -description: How to troubleshoot configuration issues in a deployed bot. +description: Learn how to troubleshoot bot errors. See how to test bots in Web Chat, check responsiveness, investigate timeout issues, and resolve problems with channels. keywords: troubleshoot, configuration, web chat, problems. -author: jonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 4/30/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: troubleshooting +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Troubleshoot bot configuration issues -The first step in troubleshooting a bot is to test it in Web Chat. This will allow you to determine if the problem is specific to your bot (bot doesn't work in any channel) or to a particular channel (bot works in some channels but not others). +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + + + +A bot can generate different types of errors, such as not being able to respond, throwing errors, or working in one channel but not in another. The first step in troubleshooting a bot is to test it in Web Chat. This will allow you to determine if the problem is specific to your bot (bot doesn't work in any channel) or to a particular channel (bot works in some channels but not others). ## Test in Web Chat -1. Open your bot resource in the [Azure Portal](https://portal.azure.com/). +1. Open your bot resource in the [Azure portal](https://portal.azure.com/). 1. Open the **Test in Web Chat** pane. 1. Send your bot a message. -![Test In Web Chat](./media/test-in-webchat.png) +:::image type="content" source="./media/test-in-webchat.png" alt-text="Test In Web Chat"::: -If the bot does not respond with the expected output, go to [Bot does not work in Web Chat](#bot-does-not-work-in-web-chat). Otherwise, go to [Bot works in Web Chat but not in other channels](#bot-works-in-web-chat-but-not-in-other-channels). +If the bot doesn't respond with the expected output, go to [Bot doesn't work in Web Chat](#bot-doesnt-work-in-web-chat). Otherwise, go to [Bot works in Web Chat but not in other channels](#bot-works-in-web-chat-but-not-in-other-channels). -## Bot does not work in Web Chat +## Bot doesn't work in Web Chat -There could be a number of reasons why a bot doesn't work. Most likely, the bot application is down and cannot receive messages, or the bot receives the messages but fails to respond. +There can be many reasons why a bot doesn't work. Most likely, the bot application is down and can't receive messages, or the bot receives the messages but fails to respond. Here are some of the possible causes: + +- The bot is down and can't be reached. +- The bot is crashing. +- The bot's endpoint is incorrect. +- The bot is successfully receiving your messages but can't respond. To see if the bot is running: 1. Open the **Overview** pane. 1. Copy the **Messaging endpoint** and paste it into your browser. -If the endpoint returns HTTP Error 405, that means the bot is reachable and the bot is able to respond to messages. You should investigate whether your bot [times out](https://github.com/daveta/analytics/blob/master/troubleshooting_timeout.md) or [fails with an HTTP 5xx error](bot-service-troubleshoot-500-errors.md). +If the endpoint returns HTTP Error 404 or 405, that means the bot is reachable and the bot is able to respond to messages. To investigate timeout issues, see [times out](https://github.com/daveta/analytics/blob/master/troubleshooting_timeout.md) or [fails with an HTTP 5xx error](bot-service-troubleshoot-500-errors.md) articles. If the endpoint returns an error "This site can't be reached" or "can't reach this page", that means that your bot is down and you need to redeploy it. @@ -43,50 +55,36 @@ If the bot works as expected in Web Chat but fails in some other channel, possib - [Troubleshoot bot configuration issues](#troubleshoot-bot-configuration-issues) - [Test in Web Chat](#test-in-web-chat) - - [Bot does not work in Web Chat](#bot-does-not-work-in-web-chat) + - [Bot doesn't work in Web Chat](#bot-doesnt-work-in-web-chat) - [Bot works in Web Chat but not in other channels](#bot-works-in-web-chat-but-not-in-other-channels) - [Channel configuration issues](#channel-configuration-issues) - [Channel-specific behavior](#channel-specific-behavior) - [Channel outage](#channel-outage) - - [Additional resources](#additional-resources) + - [Additional information](#additional-information) ### Channel configuration issues -It's possible that channel configuration parameters have been set incorrectly or have changed externally. For example, a bot has configured the Facebook channel for a particular page and the page was later deleted. The simplest solution is remove the channel and redo the channel configuration anew. - -The links below provide instructions for configuring channels supported by the Bot Framework: +It's possible that channel configuration parameters, such as the bot's username and password have been set incorrectly or have changed externally. For example, a bot was configured with the Facebook channel for a particular page and the page was later deleted. The simplest solution is to remove the channel and redo the channel configuration anew. -- [Cortana](bot-service-channel-connect-cortana.md) -- [DirectLine](bot-service-channel-connect-directline.md) -- [Email](bot-service-channel-connect-email.md) -- [Facebook](bot-service-channel-connect-facebook.md) -- [GroupMe](bot-service-channel-connect-groupme.md) -- [Kik](bot-service-channel-connect-kik.md) -- [Microsoft Teams](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-overview) -- [Skype](bot-service-channel-connect-skype.md) -- [Skype for Business](bot-service-channel-connect-skypeforbusiness.md) -- [Slack](bot-service-channel-connect-slack.md) -- [Telegram](bot-service-channel-connect-telegram.md) -- [Twilio](bot-service-channel-connect-twilio.md) +For a list of supported channels and instructions on how to configure each one, see the [Channels list](bot-service-manage-channels.md#channels-list) in **Connect a bot to channels**. ### Channel-specific behavior -Implementation of some features can differ by channel. For example, not all channels support Adaptive Cards. Most channels support Buttons, but they are rendered in a channel-specific way. If you see differences in how some message types work in different channels, consult the [channels reference](bot-service-channels-reference.md) article. +Implementation of some features can differ by channel. For example, not all channels support Adaptive Cards. Most channels support Actions (buttons), but they're rendered in a channel-specific way. If you see differences in how some message types work in different channels, consult the [channels reference](bot-service-channels-reference.md) article. -Below are some additional links that can help with individual channels: +Below are links that can help with individual channels: -- [Add bots to Microsoft Teams apps](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-overview) +- [Add bots to Microsoft Teams apps](/microsoftteams/platform/concepts/bots/bots-overview) - [Facebook: Introduction to the Messenger Platform](https://developers.facebook.com/docs/messenger-platform/introduction) -- [Principles of Cortana Skills design](https://docs.microsoft.com/cortana/skills/design-principles) - [Skype for Developers](https://dev.skype.com/bots) - [Slack: Enabling interactions with bots](https://api.slack.com/bot-users) ### Channel outage -Occasionally, some channels might have an interruption of service. Usually, such outages don't last long. However, if you suspect an outage, consult a channel web site or social media. +Occasionally, some channels might have an interruption of service. Usually, such outages don't last long. However, if you suspect an outage, consult the channel's web site or social media. -Another way to determine if a channel has an outage is to create a test bot (such as a simple Echo Bot) and add a channel. If the test bot works with some channels but not others, that would indicate that the problem is not in your production bot. +Another way to determine if a channel has an outage is to create a test bot (such as a simple Echo Bot) and add a channel. If the test bot works with some channels but not others, then the problem isn't in your production bot. -## Additional resources +## Additional information See how-to [debug a bot](bot-service-debug-bot.md) and the other debugging articles in that section. diff --git a/articles/bot-service-troubleshoot-general-problems.md b/articles/bot-service-troubleshoot-general-problems.md index a8ceb1916..4a98c88f2 100644 --- a/articles/bot-service-troubleshoot-general-problems.md +++ b/articles/bot-service-troubleshoot-general-problems.md @@ -1,311 +1,203 @@ --- -title: Troubleshooting bots - Bot Service -description: Troubleshoot general problems in bot development using technical frequently asked questions. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 09/17/2019 +title: Troubleshooting Azure AI Bot Service bots +description: These frequently asked questions can help you to troubleshoot common bot development or operational issues for bots built with Azure AI Bot Service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: troubleshooting +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Troubleshooting general problems +# General troubleshooting for Azure AI Bot Service bots + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + These frequently asked questions can help you to troubleshoot common bot development or operational issues. ## How can I troubleshoot issues with my bot? -1. Debug your bot's source code with [Visual Studio Code](debug-bots-locally-vscode.md) or [Visual Studio](https://docs.microsoft.com/visualstudio/debugger/navigating-through-code-with-the-debugger?view=vs-2017). -1. Test your bot using the [emulator](bot-service-debug-emulator.md) before you deploy it to the cloud. -1. Deploy your bot to a cloud hosting platform such as Azure and then test connectivity to your bot by using the built-in web chat control on your bot's dashboard in the Azure Portal. If you encounter issues with your bot after you deploy it to Azure, you might consider using this blog article: [Understanding Azure troubleshooting and support](https://azure.microsoft.com/blog/understanding-azure-troubleshooting-and-support/). +1. Debug your bot's source code with [Visual Studio Code](debug-bots-locally-vscode.md) or [Visual Studio](/visualstudio/debugger/navigating-through-code-with-the-debugger). +1. Test your bot using the [Bot Framework Emulator](bot-service-debug-emulator.md) before you deploy it to the cloud. +1. Deploy your bot to a cloud hosting platform such as Azure and then test connectivity to your bot by using the built-in web chat control on your bot's dashboard in the [Azure portal](https://portal.azure.com). If you encounter issues with your bot after you deploy it to Azure, you might consider using this blog article: [Understanding Azure troubleshooting and support](https://azure.microsoft.com/blog/understanding-azure-troubleshooting-and-support/). 1. Rule out [authentication][TroubleshootingAuth] as a possible issue. -1. Test your bot on Skype. This will help you to validate the end-to-end user experience. -1. Consider testing your bot on channels that have additional authentication requirements such as Direct Line or Web Chat. +1. Test your bot on Web Chat, Teams, or any other channel you intend to use with your bot. This will help you to validate the end-to-end user experience. +1. Consider testing your bot on channels that have extra authentication requirements such as Direct Line or Web Chat. 1. Review the how-to [debug a bot](bot-service-debug-bot.md) and the other debugging articles in that section. ## How can I troubleshoot authentication issues? For details about troubleshooting authentication issues with your bot, see [troubleshooting][TroubleshootingAuth] Bot Framework authentication. -## I'm using the Bot Framework SDK for .NET. How can I troubleshoot issues with my bot? - -**Look for exceptions.** -In Visual Studio 2017, go to **Debug** > **Windows** > **Exception Settings**. In the **Exceptions Settings** window, select the **Break When Thrown** checkbox next to **Common Language Runtime Exceptions**. You may also see diagnostics output in your Output window when there are thrown or unhandled exceptions. - -**Look at the call stack.** -In Visual Studio, you can choose whether or you are debugging [Just My Code](https://msdn.microsoft.com/library/dn457346.aspx) or not. Examining the full call stack may provide additional insight into any issues. - -**Ensure all dialog methods end with a plan to handle the next message.** -All dialog steps need to feed into the next step of the waterfall, or end the current dialog to pop it off the stack. If a step is not correctly handled, the conversation will not continue like you expect. Take a look at the concept article for [dialogs](v4sdk/bot-builder-concept-dialog.md) for more on dialogs. - -## Why doesn't the Typing activity do anything? -Some channels do not support transient typing updates in their client. - -## What is the difference between the Connector library and Builder library in the SDK? - -The Connector library is the exposition of the REST API. The Builder library adds the conversational dialog programming model and other features such as prompts, waterfalls, chains, and guided form completion. The Builder library also provides access to cognitive services such as LUIS. - -## What causes an error with HTTP status code 429 "Too Many Requests"? - -An error response with HTTP status code 429 indicates that too many requests have been issued in a given amount of time. The body of the response should include an explanation of the problem and may also specify the minimum required interval between requests. One possible source for this error is [ngrok](https://ngrok.com/). If you are on a free plan and running into ngrok's limits, go to the pricing and limits page on their website for more [options](https://ngrok.com/product#pricing). - -## Why aren't my bot messages getting received by the user? - -The message activity generated in response must be correctly addressed, otherwise it won’t arrive at its intended destination. In the vast majority of cases you will not need to handle this explicitly; the SDK takes care of addressing the message activity for you. - -Correctly addressing an activity means including the appropriate *conversation reference* details along with details about the sender and the recipient. In most cases, the message activity is sent in response to one that had arrived. Therefore, the addressing details can be taken from the inbound activity. - -If you examine traces or audit logs, you can check to make sure your messages are correctly addressed. If they aren't, set a breakpoint in your bot and see where the IDs are being set for your message. - -## How can I run background tasks in ASP.NET? - -In some cases, you may want to initiate an asynchronous task that waits for a few seconds and then executes some code to clear the user profile or reset conversation/dialog state. For details about how to achieve this, see [How to run Background Tasks in ASP.NET](https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx). In particular, consider using [HostingEnvironment.QueueBackgroundWorkItem](https://msdn.microsoft.com/library/dn636893(v=vs.110).aspx). - - -## How do user messages relate to HTTPS method calls? - -When the user sends a message over a channel, the Bot Framework web service will issue an HTTPS POST to the bot's web service endpoint. The bot may send zero, one, or many messages back to the user on that channel, by issuing a separate HTTPS POST to the Bot Framework for each message that it sends. +## How do I test network connectivity between bots and a channel? -## My bot is slow to respond to the first message it receives. How can I make it faster? - -Bots are web services and some hosting platforms, including Azure, automatically put the service to sleep if it does not receive traffic for a certain period of time. If this happens to your bot, it must restart from scratch the next time it receives a message, which makes its response much slower than if it was already running. - -Some hosting platforms enable you to configure your service so that it will not be put to sleep. To do this in Azure, navigate to your bot's service in the [Azure Portal](https://portal.azure.com), select **Application settings**, and then select **Always on**. This option is available in most, but not all, service plans. +You can use the IP addresses, generated by the steps below, to verify if there's any rule blocking the connection with those addresses. See section [Check firewall traces on failed connections](#check-firewall-traces-on-failed-connections). -## How can I guarantee message delivery order? - -The Bot Framework will preserve message ordering as much as possible. For example, if you send message A and wait for the completion of that HTTP operation before you initiate another HTTP operation to send message B, the Bot Framework will automatically understand that message A should precede message B. However, in general, message delivery order cannot be guaranteed since the channel is ultimately responsible for message delivery and may reorder messages. +### Test connection from bot to channel -## How can I intercept all messages between the user and my bot? +1. In your browser, go to the [Azure portal](https://ms.portal.azure.com). +1. Select your bot App Service whose connection you want to test. +1. In the left pane, in the **Development Tools** section, select **Advanced Tools**. +1. In the right pane, select **Go**. The Kudu information page is displayed. +1. In the top menu bar, select **Debug console**. Then, in the drop-down menu, select **CMD**. The Kudu bot web app console is opened. For more information, see [Kudu](https://github.com/projectkudu/kudu/wiki). -Using the Bot Framework SDK for .NET, you can provide implementations of the `IPostToBot` and `IBotToUser` interfaces to the `Autofac` dependency injection container. Using the Bot Framework SDK for any language, you can use middleware for much the same purpose. The [BotBuilder-Azure](https://github.com/Microsoft/BotBuilder-Azure) repository contains C# and Node.js libraries that will log this data to an Azure table. + :::image type="content" source="media/bot-service-troubleshoot/kudu-cmd-console.png" alt-text="kudu cmd console"::: -## Why are parts of my message text being dropped? +1. Run `nslookup directline.botframework.com` and check if the DNS resolution is working. Notice that `nslookup` (name server lookup) is a network administration command-line tool for querying the Domain Name System (DNS) to obtain domain name or IP address mapping, or other DNS records. If the DNS resolution is working, the response to this command will contain the relevant information. -The Bot Framework and many channels interpret text as if it were formatted with [Markdown](https://en.wikipedia.org/wiki/Markdown). Check to see if your text contains characters that may be interpreted as Markdown syntax. + :::image type="content" source="media/bot-service-troubleshoot/kudu-cmd-console-bot-channel-dns.png" alt-text="kudu cmd console bot channel dns"::: -## How can I support multiple bots at the same bot service endpoint? + The [WHOIS IP Lookup Tool](https://www.ultratools.com/tools/ipWhoisLookupResult) is useful to get information about IP addresses. -This [sample](https://github.com/Microsoft/BotBuilder/issues/2258#issuecomment-280506334) shows how to configure the `Conversation.Container` with the right `MicrosoftAppCredentials` and use a simple `MultiCredentialProvider` to authenticate multiple App IDs and passwords. +1. Run `curl -I directline.botframework.com`. (The option `-I` is used to obtain a response containing the header only.) Double check that an HTTP status of 301 is returned to verify there's connectivity. -## Identifiers + :::image type="content" source="media/bot-service-troubleshoot/kudu-cmd-console-http-301.png" alt-text="kudu cmd console http 301"::: -## How do identifiers work in the Bot Framework? +### Test connection from channel to bot -For details about identifiers in the Bot Framework, see the Bot Framework [guide to identifiers][BotFrameworkIDGuide]. +Because curl doesn't have access to the production site, and `directline.botframework.com` is on the public internet, you must use curl in simulation mode. Perform the steps shown below outside a Virtual Private Network (VNET), for example, using a cell phone *hotspot*. See also [What is Azure Virtual Network?](/azure/virtual-network/virtual-networks-overview). -## How can I get access to the user ID? +1. Run `nslookup ivr-sr-bot.botapps.amat.com`. The DNS resolution is working if the response to this command contains relevant information. -SMS and email messages will provide the raw user ID in the `from.Id` property. In Skype messages, the `from.Id` property will contain a unique ID for the user which differs from the user's Skype ID. If you need to connect to an existing account, you can use a sign-in card and implement your own OAuth flow to connect the user ID to your own service's user ID. + :::image type="content" source="media/bot-service-troubleshoot/kudu-cmd-console-channel-bot-dns.png" alt-text="kudu cmd console channel bot dns"::: -## Why are my Facebook user names not showing anymore? +1. Run `curl -I https://ivr-sr-bot.botapps.amat.com/api/messages` and check whether an appropriate HTTP status code is returned (for example, 405 method not allowed). The method specified in the request isn't allowed for the resource identified by the specified URI. This is just a way to check that there's connectivity. -Did you change your Facebook password? Doing so will invalidate the access token, and you will need to update your bot's configuration settings for the Facebook Messenger channel in the Azure Portal. + :::image type="content" source="media/bot-service-troubleshoot/kudu-cmd-console-http-405.png" alt-text="kudu cmd console http 405"::: -## Why is my Kik bot replying "I'm sorry, I can't talk right now"? +1. If you don't get the response from the bot, write down the client's IP address. -Bots in development on Kik are allowed 50 subscribers. After 50 unique users have interacted with your bot, any new user that attempts to chat with your bot will receive the message "I'm sorry, I can't talk right now." For more information, see [Kik documentation](https://botsupport.kik.com/hc/articles/225764648-How-can-I-share-my-bot-with-Kik-users-while-in-development-). +### Check firewall traces on failed connections -## How can I use authenticated services from my bot? - -For Azure Active Directory authentication, see the adding authentication [V3](https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-authentication?view=azure-bot-service-3.0&tabs=csharp) | [V4](https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-authentication?view=azure-bot-service-4.0&tabs=csharp). - -> [!NOTE] -> If you add authentication and security functionality to your bot, you should ensure that the patterns you implement in your code comply with the security standards that are appropriate for your application. - -## How can I limit access to my bot to a pre-determined list of users? - -Some channels, such as SMS and email, provide unscoped addresses. In these cases, messages from the user will contain the raw user ID in the `from.Id` property. - -Other channels, such as Skype, Facebook, and Slack, provide either scoped or tenanted addresses in a way that prevents a bot from being able to predict a user’s ID ahead of time. In these cases, you will need to authenticate the user via a login link or shared secret in order to determine whether or not they are authorized to use the bot. - -## Why does my Direct Line 1.1 conversation start over after every message? +Use the IP addresses from `nslookup ivr-sr-bot.botapps.amat.com` and `nslookup directline.botframework.com` and check if there's a rule blocking connection with those addresses on either direction. -If your Direct Line conversation appears to start over after every message, the `from` property is likely missing or `null` in messages that your Direct Line client sent to the bot. When a Direct Line client sends a message with the `from` property either missing or `null`, the Direct Line service automatically allocates an ID, so every message that the client sends will appear to originate from a new, different user. - -To fix this, set the `from` property in each message that the Direct Line client sends to a stable value that uniquely represents the user who is sending the message. For example, if a user is already signed-in to a webpage or app, you might use that existing user ID as the value of the `from` property in messages that the user sends. Alternatively, you might choose to generate a random user ID on page-load or on application-load, store that ID in a cookie or device state, and use that ID as the value of the `from` property in messages that the user sends. - -## What causes the Direct Line 3.0 service to respond with HTTP status code 502 "Bad Gateway"? -Direct Line 3.0 returns HTTP status code 502 when it tries to contact your bot but the request does not complete successfully. This error indicates that either the bot returned an error or the request timed out. For more information about errors that your bot generates, go to the bot's dashboard within the Azure Portal and click the "Issues" link for the affected channel. If you have Application Insights configured for your bot, you can also find detailed error information there. - -::: moniker range="azure-bot-service-3.0" - -## What is the IDialogStack.Forward method in the Bot Framework SDK for .NET? - -The primary purpose of `IDialogStack.Forward` is to reuse an existing child dialog that is often "reactive", where the child dialog (in `IDialog.StartAsync`) waits for an object `T` with some `ResumeAfter` handler. In particular, if you have a child dialog that waits for an `IMessageActivity` `T`, you can forward the incoming `IMessageActivity` (already received by some parent dialog) by using the `IDialogStack.Forward` method. For example, to forward an incoming `IMessageActivity` to a `LuisDialog`, call `IDialogStack.Forward` to push the `LuisDialog` onto the dialog stack, run the code in `LuisDialog.StartAsync` until it schedules a wait for the next message, and then immediately satisfy that wait with the forwarded `IMessageActivity`. - -`T` is usually an `IMessageActivity`, since `IDialog.StartAsync` is typically constructed to wait for that type of activity. You might use `IDialogStack.Forward` to a `LuisDialog` as a mechanism to intercept messages from the user for -some processing before forwarding the message to an existing `LuisDialog`. Alternatively, you can also use `DispatchDialog` with `ContinueToNextGroup` for that purpose. - -You would expect to find the forwarded item in the first `ResumeAfter` handler (e.g. `LuisDialog.MessageReceived`) that is scheduled by `StartAsync`. - -::: moniker-end - -## What is the difference between "proactive" and "reactive"? - -From the perspective of your bot, "reactive" means that the user initiates the conversation by sending a message to the bot, and the bot reacts by responding to that message. In contrast, "proactive" means that the bot initiates the conversation by sending the first message to the user. For example, a bot may send a proactive message to notify a user when a timer expires or an event occurs. +## I'm using the Bot Framework SDK for .NET. How can I troubleshoot issues with my bot? -## How can I send proactive messages to the user? +### Look for exceptions -For examples that show how to send proactive messages, see the [C# V4 samples](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages) and [Node.js V4 samples](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/16.proactive-messages) within the BotBuilder-Samples repository on GitHub. +In Visual Studio 2019, go to **Debug** > **Windows** > **Exception Settings**. In the **Exceptions Settings** window, select the **Break When Thrown** checkbox next to **Common Language Runtime Exceptions**. You may also see diagnostics output in your Output window when there are thrown or unhandled exceptions. -## How can I reference non-serializable services from my C# dialogs? +### Look at the call stack -There are multiple options: +In Visual Studio, you can choose whether or you're debugging [Just My Code](https://msdn.microsoft.com/library/dn457346.aspx) or not. Examining the full call stack can provide more insight into any issues. -* Resolve the dependency through `Autofac` and `FiberModule.Key_DoNotSerialize`. This is the cleanest solution. -* Use [NonSerialized](https://msdn.microsoft.com/library/system.nonserializedattribute(v=vs.110).aspx) and [OnDeserialized](https://msdn.microsoft.com/library/system.runtime.serialization.ondeserializedattribute(v=vs.110).aspx) attributes to restore the dependency on deserialization. This is the simplest solution. -* Do not store that dependency, so that it won't be serialized. This solution, while technically feasible, is not recommended. -* Use the reflection serialization surrogate. This solution may not be feasible in some cases and risks serializing too much. +### Ensure all dialog methods end with a plan to handle the next message -::: moniker range="azure-bot-service-3.0" +All dialog steps need to feed into the next step of the waterfall, or end the current dialog to pop it off the stack. If a step isn't correctly handled, the conversation won't continue like you expect. Take a look at the concept article for [dialogs](v4sdk/bot-builder-concept-dialog.md) for more on dialogs. -## Where is conversation state stored? +## What causes an error with HTTP status code 429 "Too Many Requests"? -Data in the user, conversation, and private conversation property bags is stored using the Connector's `IBotState` interface. Each property bag is scoped by the bot's ID. The user property bag is keyed by user ID, the conversation property bag is keyed by conversation ID, and the private conversation property bag is keyed by both user ID and conversation ID. +An error response with HTTP status code 429 indicates that too many requests have been issued in a given amount of time. The body of the response should include an explanation of the problem and may also specify the minimum required interval between requests. -If you use the Bot Framework SDK for .NET or the Bot Framework SDK for Node.js to build your bot, the dialog stack and dialog data will both automatically be stored as entries in the private conversation property bag. The C# implementation uses binary serialization, and the Node.js implementation uses JSON serialization. +## Why aren't my bot messages getting received by the user? -The `IBotState` REST interface is implemented by two services. +The message activity generated in response must be correctly addressed, otherwise it won't arrive at its intended destination. In most cases, you won't need to handle this explicitly; the SDK takes care of addressing the message activity for you. -* The Bot Framework Connector provides a cloud service that implements this interface and stores data in Azure. This data is encrypted at rest and does not intentionally expire. -* The Bot Framework Emulator provides an in-memory implementation of this interface for debugging your bot. This data expires when the emulator process exits. +To correctly address an activity, include the appropriate *conversation ID* details, along with details about the sender. In most cases, the message activity is sent in response to one that had arrived. Therefore, the addressing details can be taken from the inbound activity. -If you want to store this data within your data centers, you can provide a custom implementation of the state service. This can be done at least two ways: +If you examine traces or audit logs, you can check to make sure your messages are correctly addressed. If they aren't, set a breakpoint in your bot and see where the IDs are being set for your message. -* Use the REST layer to provide a custom `IBotState` service. -* Use the Builder interfaces in the language (Node.js or C#) layer. +## How can I run background tasks in ASP.NET? -> [!IMPORTANT] -> The Bot Framework State Service API is not recommended for production environments, and may be deprecated in a future release. It is recommended that you update your bot code to use the in-memory storage for testing purposes or use one of the **Azure Extensions** for production bots. For more information, see the **Manage state data** topic for [.NET](~/dotnet/bot-builder-dotnet-state.md) or [Node](~/nodejs/bot-builder-nodejs-state.md) implementation. +In some cases, you may want to initiate an asynchronous task that waits for a few seconds and then executes some code to clear the user profile or reset conversation/dialog state. For details about how to achieve this, see [How to run Background Tasks in ASP.NET](https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx). In particular, consider using [HostingEnvironment.QueueBackgroundWorkItem](https://msdn.microsoft.com/library/dn636893(v=vs.110).aspx). -::: moniker-end +## My bot is slow to respond to the first message it receives. How can I make it faster? -## What is an ETag? How does it relate to bot data bag storage? +Bots are web services and some hosting platforms, including Azure, automatically put the service to sleep if it doesn't receive traffic for a certain period of time. If this happens to your bot, it must restart from scratch the next time it receives a message, which makes its response much slower than if it was already running. -An [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) is a mechanism for [optimistic concurrency control](https://en.wikipedia.org/wiki/Optimistic_concurrency_control). The bot data bag storage uses ETags to prevent conflicting updates to the data. An ETag error with HTTP status code 412 "Precondition Failed" indicates that there were multiple "read-modify-write" sequences executing concurrently for that bot data bag. +Some hosting platforms enable you to configure your service so that it won't be put to sleep. If your bot is hosted on Azure AI Bot Service Web Apps, go to your bot's service in the [Azure portal](https://portal.azure.com), select **Application settings**, and then select **Always on**. This option is available in most, but not all, service plans. -The dialog stack and state are stored in bot data bags. For example, you might see the "Precondition Failed" ETag error if your bot is still processing a previous message when it receives a new message for that conversation. +## How can I guarantee message delivery order? -## What causes an error with HTTP status code 412 "Precondition Failed" or HTTP status code 409 "Conflict"? +The Bot Framework will preserve message ordering as much as possible. For example, if you send message A and wait for the completion of that HTTP operation before you initiate another HTTP operation to send message B. Some channels, such as SMS and email, don't guarantee to order in which the user will receive messages. -The Connector's `IBotState` service is used to store the bot data bags (i.e., the user, conversation, and private bot data bags, where the private bot data bag includes the dialog stack "control flow" state). Concurrency control in the `IBotState` service is managed by optimistic concurrency via ETags. If there is an update conflict (due to a concurrent update to a single bot data bag) during a "read-modify-write" sequence, then: +## Why are parts of my message text being dropped? -* If ETags are preserved, an error with HTTP status code 412 "Precondition Failed" is thrown from the `IBotState` service. This is the default behavior in the Bot Framework SDK for .NET. -* If ETags are not preserved (i.e., ETag is set to `\*`), then the "last write wins" policy will be in effect, which prevents the "Precondition Failed" error but risks data loss. This is the default behavior in the Bot Framework SDK for Node.js. +The Bot Framework and many channels interpret text as if it were formatted with [Markdown](https://en.wikipedia.org/wiki/Markdown). Check to see if your text contains characters that may be interpreted as Markdown syntax. -## How can I fix "Precondition Failed" (412) or "Conflict" (409) errors? +## How can I support multiple bots at the same bot service endpoint? -These errors indicate that your bot processed multiple messages for the same conversation at once. If your bot is connected to services that require precisely ordered messages, -you should consider locking the conversation state to make sure messages are not processed in parallel. +This [sample](https://github.com/Microsoft/BotBuilder/issues/2258#issuecomment-280506334) shows how to configure the `Conversation.Container` with the right `MicrosoftAppCredentials` and use a simple `MultiCredentialProvider` to authenticate multiple App IDs and passwords. -::: moniker range="azure-bot-service-3.0" +## How do identifiers work in the Bot Framework? -The Bot Framework SDK for .NET provides a mechanism (class `LocalMutualExclusion` which implements `IScope`) to -pessimistically serialize the handling of a single conversations with an in-memory semaphore. You could extend this implementation to use a Redis lease, scoped by the conversation address. +For details about identifiers in the Bot Framework, see the Bot Framework [guide to identifiers][BotFrameworkIDGuide]. -If your bot is not connected to external services or if processing messages in parallel from the same conversation is acceptable, you can add this code to ignore any collisions that occur in the Bot State API. This will allow the last reply to set the conversation state. +## How can I get access to the user ID? -```cs -var builder = new ContainerBuilder(); -builder - .Register(c => new CachingBotDataStore(c.Resolve(), CachingBotDataStoreConsistencyPolicy.LastWriteWins)) - .As>() - .AsSelf() - .InstancePerLifetimeScope(); -builder.Update(Conversation.Container); -``` +Bot Framework channels present the user's ID in the `from.Id` field of any Activity sent by the user. SMS and email messages will provide the raw user ID in this property. Some channels obscure the `from.Id` property so it contains unique ID for the user, which differs from the user's ID in the channel. If you need to connect to an existing account, you can use a sign-in card and implement your own OAuth flow to connect the user ID to your own service's user ID. -## How do I version the bot data stored through the State API? +## Why are my Facebook user names not showing anymore? -> [!IMPORTANT] -> The Bot Framework State Service API is not recommended for production environments or v4 bots, and may be fully deprecated in a future release. It is recommended that you update your bot code to use the in-memory storage for testing purposes or use one of the **Azure Extensions** for production bots. For more information, see the [Manage state data](v4sdk/bot-builder-howto-v4-state.md) topic. +Did you change your Facebook password? Doing so will invalidate the access token, and you'll need to update your bot's configuration settings for the Facebook Messenger channel in the [Azure portal](https://portal.azure.com). -The State service enables you to persist progress through the dialogs in a conversation so that a user can return to a conversation with a bot later without losing their position. To preserve this, the bot data property bags that are stored via the State API are not automatically cleared when you modify the bot's code. You should decide whether or not the bot data should be cleared, based upon whether your modified code is compatible with older versions of your data. +## How can I use authenticated services from my bot? -* If you want to manually reset the conversation's dialog stack and state during development of your bot, you can use the `/deleteprofile` command to delete state data. Make sure to include the leading space in this command, to prevent the channel from interpreting it. -* After your bot has been deployed to production, you can version your bot data so that if you bump the version, the associated state data is cleared. With the Bot Framework SDK for Node.js, this can be accomplished using middleware and with the Bot Framework SDK for .NET, this can be accomplished using an `IPostToBot` implementation. +For Microsoft Entra ID authentication, see the [Add authentication to your bot](v4sdk/bot-builder-authentication.md) tutorial. > [!NOTE] -> If the dialog stack cannot be deserialized correctly, due to serialization format changes or because the code has changed too much, the conversation state will be reset. - -::: moniker-end - -## What are the possible machine-readable resolutions of the LUIS built-in date, time, duration, and set entities? - -For a list of examples, see the [Pre-built entities section][LUISPreBuiltEntities] of the LUIS documentation. - -## How can I use more than the maximum number of LUIS intents? - -You might consider splitting up your model and calling the LUIS service in series or parallel. - -## How can I use more than one LUIS model? +> If you add authentication and security functionality to your bot, you should ensure that the patterns you implement in your code comply with the security standards that are appropriate for your application. -Both the Bot Framework SDK for Node.js and the Bot Framework SDK for .NET support calling multiple LUIS models from a single LUIS intent dialog. Keep in mind the following caveats: +## How can I limit access to my bot to a pre-determined list of users? -* Using multiple LUIS models assumes the LUIS models have non-overlapping sets of intents. -* Using multiple LUIS models assumes the scores from different models are comparable, to select the "best matched intent" across multiple models. -* Using multiple LUIS models means that if an intent matches one model, it will also strongly match the "none" intent of the other models. You can avoid selecting the "none" intent in this situation; the Bot Framework SDK for Node.js will automatically scale down the score for "none" intents to avoid this issue. +Some channels, such as SMS and email, provide unscoped addresses. In these cases, messages from the user will contain the raw user ID in the `from.Id` property. -## Where can I get more help on LUIS? +Other channels, such as Facebook and Slack, provide either scoped or tenanted addresses in a way that prevents a bot from being able to predict a user's ID ahead of time. In these cases, you'll need to authenticate the user via a login link or shared secret in order to determine whether or not they're authorized to use the bot. -* [Introduction to Language Understanding (LUIS) - Microsoft Cognitive Services](https://www.youtube.com/watch?v=jWeLajon9M8) (video) -* [Advanced Learning Session for Language Understanding (LUIS)](https://www.youtube.com/watch?v=39L0Gv2EcSk) (video) -* [LUIS documentation](/azure/cognitive-services/LUIS/Home) -* [Language Understanding Forum](https://social.msdn.microsoft.com/forums/azure/home?forum=LUIS) +## Why does my Direct Line 1.1 conversation start over after every message? +> [!NOTE] +> +> This section doesn't apply to the latest version of the Direct Line protocol, 3.0 -## What are some community-authored dialogs? +If your Direct Line conversation appears to start over after every message, the `from` property is likely missing or `null` in messages that your Direct Line client sent to the bot. When a Direct Line client sends a message with the `from` property either missing or `null`, the Direct Line service automatically allocates an ID, so every message that the client sends will appear to originate from a new, different user. -* [BotAuth](https://www.nuget.org/packages/BotAuth) - Azure Active Directory authentication -* [BestMatchDialog](http://www.garypretty.co.uk/2016/08/01/bestmatchdialog-for-microsoft-bot-framework-now-available-via-nuget/) - Regular expression-based dispatch of user text to dialog methods +To fix this, set the `from` property in each message that the Direct Line client sends to a stable value that uniquely represents the user who is sending the message. For example, if a user is already signed-in to a webpage or app, you might use that existing user ID as the value of the `from` property in messages that the user sends. Alternatively, you might choose to generate a random user ID on page-load or on application-load, store that ID in a cookie or device state, and use that ID as the value of the `from` property in messages that the user sends. -## What are some community-authored templates? +## What causes the Direct Line 3.0 service to respond with HTTP status code 502 "Bad Gateway"? -* [ES6 BotBuilder](https://github.com/brene/botbuilder-es6-template) - ES6 Bot Builder template +Direct Line 3.0 returns HTTP status code 502 when it tries to contact your bot but the request doesn't complete successfully. This error indicates that either the bot returned an error or the request timed out. For more information about errors that your bot generates, go to the bot's dashboard within the [Azure portal](https://portal.azure.com) and select the "Issues" link for the affected channel. If you configured Application Insights for your bot, you can also find detailed error information there. ## Why do I get an Authorization_RequestDenied exception when creating a bot? -Permission to create Azure Bot Service bots are managed through the Azure Active Directory (AAD) portal. If permissions are not properly configured in the [AAD portal](https://aad.portal.azure.com), users will get the **Authorization_RequestDenied** exception when trying to create a bot service. +Permission to create Azure AI Bot Service bots are managed through the Microsoft Entra ID portal. If permissions aren't properly configured in the [Microsoft Entra ID admin center](https://aad.portal.azure.com), users will get the **Authorization_RequestDenied** exception when trying to create a bot service. -First check whether you are a "Guest" of the directory: +First check whether you're a "Guest" of the directory: 1. Sign-in to [Azure portal](https://portal.azure.com). -2. Click **All services** and search for *active*. -3. Select **Azure Active Directory**. -4. Click **Users**. -5. Find the user from the list and ensure that the **User Type** is not a **Guest**. +1. Select **All services** and search for *active*. +1. Select **Microsoft Entra ID**. +1. Select **Users**. +1. Find the user from the list and ensure that the **User Type** isn't a **Guest**. -![Azure Active Directory User-type](~/media/azure-active-directory/user_type.png) +:::image type="content" source="media/azure-active-directory/user_type.png" alt-text="Microsoft Entra ID User-type"::: -Once you verified that you are not a **Guest**, then to ensure that users within an active directory can create bot service, the directory administrator needs to configure the following settings: +Once you verified that you're not a **Guest**, then to ensure that users within an active directory can create bot service, the directory administrator needs to configure the following settings: -1. Sign-in to [AAD portal](https://aad.portal.azure.com). Go to **Users and groups** and select **User settings**. -2. Under **App registration** section, set **Users can register applications** to **Yes**. This allows users in your directory to create bot service. -3. Under the **External users** section, set **Guest users permissions are limited** to **No**. This allows guest users in your directory to create bot service. +1. Sign-in to [Microsoft Entra ID admin center](https://aad.portal.azure.com). Go to **Users and groups** and select **User settings**. +1. Under **App registration** section, set **Users can register applications** to **Yes**. This allows users in your directory to create bot service. +1. Under the **External users** section, set **Guest users permissions are limited** to **No**. This allows guest users in your directory to create bot service. -![Azure Active Directory Admin Center](~/media/azure-active-directory/admin_center.png) +:::image type="content" source="media/azure-active-directory/admin_center.png" alt-text="Microsoft Entra ID Admin Center"::: ## Why can't I migrate my bot? -If you are having issues migrating your bot, it might be because the bot belongs to a directory other than your default directory. Try these steps: +If your bot is registered in dev.botframework.com, and you want to migrate it to Azure, but are having issues migrating your bot, it might be because the bot belongs to a directory other than your default directory. Try these steps: -1. From the target directory, add a new user (via email address) that is not a member of the default directory, grant the user contributor role on the subscriptions that are the target of the migration. +1. From the target directory, add a new user (via email address) that isn't a member of the default directory, grant the user contributor role on the subscriptions that are the target of the migration. -2. From [Dev Portal](https://dev.botframework.com), add the user’s email address as co-owners of the bot that should be migrated. Then sign out. +1. From [Dev Portal](https://dev.botframework.com), add the user's email address as co-owners of the bot that should be migrated. Then sign out. -3. Sign in to [Dev Portal](https://dev.botframework.com) as the new user and proceed to migrate the bot. +1. Sign in to [Dev Portal](https://dev.botframework.com) as the new user and proceed to migrate the bot. ## Where can I get more help? -* Leverage the information in previously answered questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework), or post your own questions using the `botframework` tag. Please note that Stack Overflow has guidelines such as requiring a descriptive title, a complete and concise problem statement, and sufficient details to reproduce your issue. Feature requests or overly broad questions are off-topic; new users should visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask) for more details. +* Search for previously answered questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework), or post your own questions using the `botframework` tag. Stack Overflow has guidelines such as requiring a descriptive title, a complete and concise problem statement, and sufficient details to reproduce your issue. Feature requests or overly broad questions are off-topic; new users should visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask) for more details. * Consult [BotBuilder issues](https://github.com/Microsoft/BotBuilder/issues) in GitHub for information about known issues with the Bot Framework SDK, or to report a new issue. -* Leverage the information in the BotBuilder community discussion on [Gitter](https://gitter.im/Microsoft/BotBuilder). - - +* The BotBuilder community discussion on [Gitter](https://gitter.im/Microsoft/BotBuilder). - -[LUISPreBuiltEntities]: /azure/cognitive-services/luis/pre-builtentities [BotFrameworkIDGuide]: bot-service-resources-identifiers-guide.md -[StateAPI]: ~/rest-api/bot-framework-rest-state.md [TroubleshootingAuth]: bot-service-troubleshoot-authentication-problems.md - diff --git a/articles/bot-service-troubleshoot-index.md b/articles/bot-service-troubleshoot-index.md new file mode 100644 index 000000000..12594d5f2 --- /dev/null +++ b/articles/bot-service-troubleshoot-index.md @@ -0,0 +1,69 @@ +--- +title: Bot Framework TroubleshootIndex - Bot Service +description: TroubleshootIndex Bot Framework Index. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: troubleshooting +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +# Troubleshoot index + +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] + + + +The following are some common questions that address problems you might face. +In case you don't find the answer you're looking for, you can post your questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) using the `botframework` tag. If you're a new user, visit the [Stack Overflow Help Center](https://stackoverflow.com/help/how-to-ask). + +## Troubleshoot general + +- [How can I troubleshoot issues with my bot?](bot-service-troubleshoot-general-problems.md#how-can-i-troubleshoot-issues-with-my-bot) +- [How can I troubleshoot authentication issues?](bot-service-troubleshoot-general-problems.md#how-can-i-troubleshoot-authentication-issues) +- [I'm using the Bot Framework SDK for .NET. How can I troubleshoot issues with my bot?](bot-service-troubleshoot-general-problems.md#im-using-the-bot-framework-sdk-for-net-how-can-i-troubleshoot-issues-with-my-bot) +- [How do I test network connectivity between bots and a channel?](bot-service-troubleshoot-general-problems.md#how-do-i-test-network-connectivity-between-bots-and-a-channel) +- [What causes an error with HTTP status code 429 "Too Many Requests"?](bot-service-troubleshoot-general-problems.md#what-causes-an-error-with-http-status-code-429-too-many-requests) +- [Why aren't my bot messages getting received by the user?](bot-service-troubleshoot-general-problems.md#why-arent-my-bot-messages-getting-received-by-the-user) +- [How can I run background tasks in ASP.NET?](bot-service-troubleshoot-general-problems.md#how-can-i-run-background-tasks-in-aspnet) +- [My bot is slow to respond to the first message it receives. How can I make it faster?](bot-service-troubleshoot-general-problems.md#my-bot-is-slow-to-respond-to-the-first-message-it-receives-how-can-i-make-it-faster) +- [How can I guarantee message delivery order?](bot-service-troubleshoot-general-problems.md#how-can-i-guarantee-message-delivery-order) +- [Why are parts of my message text being dropped?](bot-service-troubleshoot-general-problems.md#why-are-parts-of-my-message-text-being-dropped) +- [How can I support multiple bots at the same bot service endpoint?](bot-service-troubleshoot-general-problems.md#how-can-i-support-multiple-bots-at-the-same-bot-service-endpoint) +- [How do identifiers work in the Bot Framework?](bot-service-troubleshoot-general-problems.md#how-do-identifiers-work-in-the-bot-framework) +- [How can I get access to the user ID?](bot-service-troubleshoot-general-problems.md#how-can-i-get-access-to-the-user-id) +- [Why are my Facebook user names not showing anymore?](bot-service-troubleshoot-general-problems.md#why-are-my-facebook-user-names-not-showing-anymore) +- [How can I use authenticated services from my bot?](bot-service-troubleshoot-general-problems.md#how-can-i-use-authenticated-services-from-my-bot) +- [How can I limit access to my bot to a pre-determined list of users?](bot-service-troubleshoot-general-problems.md#how-can-i-limit-access-to-my-bot-to-a-pre-determined-list-of-users) +- [Why does my Direct Line 1.1 conversation start over after every message?](bot-service-troubleshoot-general-problems.md#why-does-my-direct-line-11-conversation-start-over-after-every-message) +- [What causes the Direct Line 3.0 service to respond with HTTP status code 502 "Bad Gateway"?](bot-service-troubleshoot-general-problems.md#what-causes-the-direct-line-30-service-to-respond-with-http-status-code-502-bad-gateway) +- [Why do I get an Authorization_RequestDenied exception when creating a bot?](bot-service-troubleshoot-general-problems.md#why-do-i-get-an-authorization_requestdenied-exception-when-creating-a-bot) +- [Why can't I migrate my bot?](bot-service-troubleshoot-general-problems.md#why-cant-i-migrate-my-bot) +- [Where can I get more help?](bot-service-troubleshoot-general-problems.md#where-can-i-get-more-help) + +## Configuration + +- [Test in Web Chat](bot-service-troubleshoot-bot-configuration.md#test-in-web-chat) +- [Bot doesn't work in Web Chat](bot-service-troubleshoot-bot-configuration.md#bot-doesnt-work-in-web-chat) +- [Bot works in Web Chat but not in other channels](bot-service-troubleshoot-bot-configuration.md#bot-works-in-web-chat-but-not-in-other-channels) + - [Channel configuration issues](bot-service-troubleshoot-bot-configuration.md#channel-configuration-issues) + - [Channel-specific behavior](bot-service-troubleshoot-bot-configuration.md#channel-specific-behavior) + - [Channel outage](bot-service-troubleshoot-bot-configuration.md#channel-outage) + +## HTTP 500 Errors + +- [Enable Application Insights on ASP.NET](bot-service-troubleshoot-500-errors.md#enable-application-insights-for-aspnet) +- [Enable Application Insights on Node.js](bot-service-troubleshoot-500-errors.md#enable-application-insights-for-nodejs) +- [Query for exceptions](bot-service-troubleshoot-500-errors.md#query-for-exceptions) +- [No Application Insights events](bot-service-troubleshoot-500-errors.md#no-application-insights-events) + +## Authentication + +- [App ID and password](bot-service-troubleshoot-authentication-problems.md#app-id-and-password) +- [Step 1: Disable security and test on localhost](bot-service-troubleshoot-authentication-problems.md#step-1-disable-security-and-test-on-localhost) +- [Step 2: Verify your bot's app ID and password](bot-service-troubleshoot-authentication-problems.md#step-2) +- [Step 3: Enable security and test on localhost](bot-service-troubleshoot-authentication-problems.md#step-3-enable-security-and-test-on-localhost) +- [Step 4: Test your bot in the cloud](bot-service-troubleshoot-authentication-problems.md#step-4-test-your-bot-in-the-cloud) diff --git a/articles/breadcrumb/TOC.yml b/articles/breadcrumb/TOC.yml index db616db0c..1fca34c9b 100644 --- a/articles/breadcrumb/TOC.yml +++ b/articles/breadcrumb/TOC.yml @@ -1,17 +1,14 @@ -- name: Docs - href: / - homepage: / +- name: Learn + tocHref: / + topicHref: / items: - - name: Bot Service - tocHref: /Bot-Framework/ - topicHref: /Bot-Framework/index + - name: Azure + tocHref: /azure/ + topicHref: /azure/index items: - - name: Bot Builder SDK for .NET - tocHref: /Bot-Framework/dotnet - topicHref: /Bot-Framework/dotnet/ - - name: Bot Builder SDK for Node.js - tocHref: /Bot-Framework/nodejs - topicHref: /Bot-Framework/nodejs/ - - name: Bot Framework REST API - tocHref: /Bot-Framework/rest-api - topicHref: /Bot-Framework/rest-api/ \ No newline at end of file + - name: AI Services + tocHref: /azure/ai-services/ + topicHref: /azure/ai-services/ + items: + - name: AI Bot Service + href: /azure/bot-service/ diff --git a/articles/channel-connect-teams.md b/articles/channel-connect-teams.md index 6317867d1..a11afce1e 100644 --- a/articles/channel-connect-teams.md +++ b/articles/channel-connect-teams.md @@ -1,36 +1,72 @@ --- -title: Connect a bot to Teams - Bot Service -description: Learn how to configure a bot for access through the Team. -keywords: Teams, bot channel, configure Teams -author: kamrani -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 08/26/2019 +title: Connect a Bot Framework bot to Microsoft Teams +description: Learn how to configure bots to connect to the Microsoft Teams channel and communicate with users via Teams. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Connect a bot to Teams -To add the Microsoft Teams channel, open the bot in the [Azure portal](https://portal.azure.com), click the **Channels** blade, and then -click **Teams**. +# Connect a bot to Microsoft Teams -![Add Teams channel](media/teams/connect-teams-channel.png) +[!INCLUDE [applies-to-v4](includes/applies-to-v4-current.md)] -Next, click **Save**. +You can configure your bot to communicate with people via Microsoft Teams. This article describes how to create a Teams app in Teams, connect your bot to your Teams app in Azure, and then test your bot in Teams. -![Save Teams channel](media/teams/save-teams-channel.png) +## Prerequisites -After adding the Teams channel, go to the **Channels** page and click on **Get bot embed code**. +- An Azure subscription. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- A bot published to Azure that you want to connect to Teams. +- A developer tenant in Teams with custom app uploading or sideloading enabled. For more information, see [Prepare your Microsoft 365 tenant](/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant). +- A valid Teams app package. For more information, see [Upload your app in Microsoft Teams](/microsoftteams/platform/concepts/deploy-and-publish/apps-upload). -![Get embed code](media/teams/get-embed-code.png) +## Configure your bot in Azure -- Copy the _https_ part of the code that is showin in the **Get bot embed code** dialog. For example, `https://teams.microsoft.com/l/chat/0/0?users=28:b8a22302e-9303-4e54-b348-343232`. +1. Open the [Azure portal](https://portal.azure.com/). +1. Open the Azure Bot resource blade for your bot. +1. Open **Channels** and select **Microsoft Teams**: + 1. Read and agree to the terms of service. + 1. On the **Messaging** tab, select the cloud environment for your bot. For more information, see the [Post build](/microsoftteams/platform/concepts/app-fundamentals-overview) section of **Plan your app with Teams features**. + 1. Select **Apply**. +1. Select **Get bot embed code**, locate the embed code for Teams, and then copy the _https_ part of the code. For example, `https://teams.microsoft.com/l/chat/0/0?users=28:b8a22302e-9303-4e54-b348-343232`. You can use this code to test the bot in Teams. -- In the browser, paste this address and then choose the Microsoft Teams app (client or web) that you use to add the bot to Teams. You should be able to see the bot listed as a contact that you can send messages to and recieves messages from in Microsoft Teams. +> [!TIP] +> +> - The **Calling** tab supports the Teams calling feature. For more information, see [Register calls and meetings bot for Microsoft Teams](/microsoftteams/platform/bots/calls-and-meetings/registering-calling-bot). +> - The **Publish** tab contains information about how to publish your Teams app to the Teams Store. +> - The **Microsoft Azure operated by 21Vianet** does not support the **Get bot embed code** feature. To test the Teams channel, create the Teams app and deploy it using the steps mentioned below. -> [!IMPORTANT] -> Adding a bot by GUID, for anything other than testing purposes, is not recommended. Doing so severely limits the functionality of a bot. Bots in production should be added to Teams as part of an app. See [Create a bot](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-create) and [Test and debug your Microsoft Teams bot](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-test). +## Test your bot in Teams +Bots in production should be added to Teams as part of a Teams app. For more information, see [Test your app](/microsoftteams/platform/concepts/build-and-test/test-app-overview). + +> [!IMPORTANT] +> Adding a bot by GUID, for anything other than testing purposes, isn't recommended. Doing so severely limits the functionality of a bot. Bots in production should be added to Teams as part of an app. + +1. In your browser, open the URL you copied from your embed code, then choose the Microsoft Teams app (client or web) that you use to add the bot to Teams. You should be able to see the bot listed as a contact that you can send messages to and receive messages from in Microsoft Teams. +1. Interact with your bot in Teams. + +> [!TIP] +> Use one bot channel registration per environment, since your endpoint changes when you switch between local development, staging, and production environments. +> +> Deleting the Teams channel registration will cause a new pair of keys to be generated when it's re-enabled. This invalidates all 29:xxx and a:xxx IDs that the bot may have stored for proactive messaging. + +## Publish your bot in Teams + +For instructions on how to publish your app, see the Teams overview of how to [Distribute your Microsoft Teams app](/microsoftteams/platform/concepts/deploy-and-publish/apps-publish-overview). It and the associated articles cover how to: + +- Choose and configure install options for your bot +- Create your Teams app manifest, icon, and package +- Upload your app to Teams +- Publish your app to your org or to the Teams store ## Additional information -For Microsoft Teams specific information, see Teams [documentation](https://docs.microsoft.com/microsoftteams/platform/overview). + +- For more about Teams app development, see [Build apps for Microsoft Teams](/microsoftteams/platform/overview) and [Get started](/microsoftteams/platform/get-started/get-started-overview). +- For more about creating bots for Teams, see [Bots in Microsoft Teams](/microsoftteams/platform/bots/what-are-bots). +- For more about publishing and testing a bot in Teams, see [Distribute your Microsoft Teams app](/microsoftteams/platform/concepts/deploy-and-publish/apps-publish-overview) and [Test your app](/microsoftteams/platform/concepts/build-and-test/test-app-overview). +- To provide feedback and find additional resources, see [Microsoft Teams developer community channels](/microsoftteams/platform/feedback). diff --git a/articles/channels/TOC.md b/articles/channels/TOC.md deleted file mode 100644 index 714773bbc..000000000 --- a/articles/channels/TOC.md +++ /dev/null @@ -1,9 +0,0 @@ -# [Implement channel-specific functionality](../dotnet/bot-builder-dotnet-channeldata.md) -# [Build a Cortana skill](../dotnet/bot-builder-dotnet-cortana-skill.md) -# [Conduct audio calls with Skype](../dotnet/bot-builder-dotnet-audio-calls.md) -# Conduct real-time media calls with Skype -## [Real-time media calling concepts](../dotnet/bot-builder-dotnet-real-time-media-concepts.md) -## [Requirements for real-time media bots](../dotnet/bot-builder-dotnet-real-time-media-requirements.md) -## [Build a real-time media bot](../dotnet/bot-builder-dotnet-real-time-audio-video-call-overview.md) -## [Deploy a real-time media bot](../dotnet/bot-builder-dotnet-real-time-deploy-visual-studio.md) - \ No newline at end of file diff --git a/articles/conversation-designer/conversation-designer-adaptive-cards.md b/articles/conversation-designer/conversation-designer-adaptive-cards.md deleted file mode 100644 index a450ddce0..000000000 --- a/articles/conversation-designer/conversation-designer-adaptive-cards.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Configure adaptive cards - Bot Service -description: Learn how to configure adaptive cards. -author: vkannan -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -ROBOTS: NoIndex, NoFollow ---- - -# Configure adaptive cards -> [!IMPORTANT] -> Conversation Designer is not available to all customers yet. More details on -> availability of Conversation Designer will come later this year. - -Adaptive Cards is a new schema that defines rich UI cards for use in several -different endpoints including Microsoft Bot Framework channels. - -Conversation Designer provides a deeply integrated authoring environment to author, preview, and use adaptive cards in your bots. - -Adaptive cards can be defined in several different key places. - -- A simple response to action for a task. -- In feedback state in a dialogue. -- In prompt states in a dialogue. Note that prompts can have separate cards: one for the response and another for re-prompting. - -To define an adaptive card, navigate to the relevant editor. Browse and choose from one of the existing Adaptive Card -Templates or build your own in the JSON code editor. - -As you are building a card, a rich preview of the card is rendered in the authoring portal. - -> [!NOTE] -> Features of adaptive cards remain under ongoing development. All channels do not support all adaptive card features at this time. To see which features each channel supports, see the Channel status section. - -## Input form - -Adaptive cards can contain input forms. In Conversation Designer, forms are integrated with task entities. For example, if a field has an `id` of **myName** and the form `Submit` action is performed, a `taskEntity` with the name **myName** will be created and will contain the value of the field. - -The code snippet below shows how the **myName** entity is defined in code: - -```javascript -{ - "type": "Input.Text", - "id": "myName", - "placeholder": "Last, First" -} -``` - -Additionally, if a field has an id of `@task` then the value of the field will be used as a task name. When this field is triggered (e.g.: a button click), then the named task will be executed. - -Take this snippet code for example: - -```javascript -{ - 'type': 'Action.Submit', - 'title': 'Search', - 'speak': 'Search', - 'data': { - '@task': 'Hotel Search' - } -} -``` - -When this button is clicked, a submit action is triggered and the `context.sticky` will be set to `Hotel Search`. This will result in the **Hotel Search** task to execute. To use this feature, make sure the `@task` matches a task name that you have defined in Conversation Designer. - -## Use entities and language generation templates -Adaptive cards support full language generation resolution. - -* `entityName` uses entities inside the card. -* `responseTemplateName` uses simple or conditional response templates inside the card. - -You can learn more about adaptive cards here TODO: Insert link to adaptive cards schema documentation --> - -## Sample adaptive card payload - -The following JSON shows the payload of an adaptive card. - -```json -{ - "$schema": "https://microsoft.github.io/AdaptiveCards/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "speak": "Serious Pie is a Pizza restaurant which is rated 9.3 by customers.", - "type": "ColumnSet", - "columns": [ - { - "type": "Column", - "size": "2", - "items": [ - { - "type": "TextBlock", - "text": "[Greeting], [TimeOfDayTemplate], You can eat in {location}", - "weight": "bolder", - "size": "extraLarge" - }, - { - "type": "TextBlock", - "text": "9.3 · $$ · Pizza", - "isSubtle": true - }, - { - "type": "TextBlock", - "text": "[builtin.feedback.display]", - "wrap": true - } - ] - }, - { - "type": "Column", - "size": "1", - "items": [ - { - "type": "Image", - "url": "http://res.cloudinary.com/sagacity/image/upload/c_crop,h_670,w_635,x_0,y_0/c_scale,w_640/v1397425743/Untitled-4_lviznp.jpg", - "size":"auto" - } - ] - } - ] - } - ], - "actions": [ - { - "type": "Action.Http", - "method": "POST", - "title": "More Info", - "url": "http://foo.com" - }, - { - "type": "Action.Http", - "method": "POST", - "title": "View on Foursquare", - "url": "http://foo.com" - } - ] -} -``` - diff --git a/articles/debug/TOC.md b/articles/debug/TOC.md deleted file mode 100644 index 0998e9a6a..000000000 --- a/articles/debug/TOC.md +++ /dev/null @@ -1,6 +0,0 @@ - -# [Debug a bot using IDE](../bot-service-debug-bot.md) -# [Debug with the Bot Framework Emulator](../bot-service-debug-emulator.md) -# [Debug a bot with inspection middleware](../bot-service-debug-inspection-middleware.md) -# [Debug your bot using transcript files](../v4sdk/bot-builder-debug-transcript.md) -# [Test a Cortana skill](../bot-service-debug-cortana-skill.md) diff --git a/articles/design/TOC.md b/articles/design/TOC.md deleted file mode 100644 index 585e84c67..000000000 --- a/articles/design/TOC.md +++ /dev/null @@ -1,11 +0,0 @@ -# [Principles of bot design](../bot-service-design-principles.md) -# [First interaction](../bot-service-design-first-interaction.md) -# [Design and control conversation flow](../bot-service-design-conversation-flow.md) -# [Design bot navigation](../bot-service-design-navigation.md) -# [Design the user experience](../bot-service-design-user-experience.md) -# Patterns -## [Task automation](../bot-service-design-pattern-task-automation.md) -## [Knowledge base](../bot-service-design-pattern-knowledge-base.md) -## [Handoff to human](../bot-service-design-pattern-handoff-human.md) -## [Bots in apps](../bot-service-design-pattern-embed-app.md) -## [Bots in websites](../bot-service-design-pattern-embed-web-site.md) diff --git a/articles/directline-speech-bot.md b/articles/directline-speech-bot.md deleted file mode 100644 index a5cc0efc5..000000000 --- a/articles/directline-speech-bot.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Develop DirectLine Speech Bot - Bot Service -description: Develop DirectLine Speech Bot -keywords: develop Direct Line speech bot, speech bot -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/01/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Use Direct Line Speech in your bot - -[!INCLUDE [applies-to-v4](includes/applies-to.md)] - -Direct Line Speech uses a new WebSocket based streaming capability of Bot Framework to exchange messages between the Direct Line Speech channel and your bot. After enabling the Direct Line Speech channel in the Azure Portal, you will need to update your bot to listen for and accept these WebSocket connections. These instructions explain how to do this. - -## Step 1: Upgrade to the 4.6 SDK - -For Direct Line Speech ensure you are using version 4.6 or above the Bot Builder SDK. - -## Step 2: Update your .NET Core bot code if your bot uses AddBot and UseBotFramework instead of a BotController - -If you have created a bot using v4 of the Bot Builder SDK prior to version 4.3.2, your bot likely does not include a BotController but instead uses the AddBot() and UseBotFramework() methods in the Startup.cs file to expose the POST endpoint where the bot receives messages. To expose the new streaming endpoint, you will need to add a BotController and remove the AddBot() and UseBotFramework() methods. These instructions walk through the changes that need to be made. If you already have these changes, continue to the next step. - -Add a new MVC controller to your bot project by adding a file called BotController.cs. Add the controller code to this file: - -```cs - -[Route("api/messages")] - -[ApiController] - -public class BotController : ControllerBase -{ - private readonly IBotFrameworkHttpAdapter _adapter; - private readonly IBot _bot; - public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) - { - _adapter = adapter; - - _bot = bot; - } - - [HttpPost, HttpGet] - public async Task ProcessMessageAsync() - { - await _adapter.ProcessAsync(Request, Response, _bot); - } -} -``` - -In the **Startup.cs** file, locate the Configure method. Remove the `UseBotFramework()` line and make sure you have these lines to `UseWebSockets`: - -```cs - -public void Configure(IApplicationBuilder app, IHostingEnvironment env) -{ - ... - app.UseDefaultFiles(); - app.UseStaticFiles(); - app.UseWebSockets(); - app.UseMvc(); - ... -} -``` - -Also in the Startup.cs file, locate the ConfigureServices method. Remove the `AddBot()` line and make sure you have lines for adding your `IBot` and a `BotFrameworkHttpAdapter`: - -```cs - -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - services.AddSingleton(); - services.AddSingleton(); - - // Create the Bot Framework Adapter. - services.AddSingleton(); - - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` - -The remainder of your bot code stays the same! - -## Step3: Ensure WebSockets are enabled - -When you create a new bot from the Azure Portal using one of the templates such as EchoBot, you will get a bot that includes an ASP.NET MVC controller that exposes a GET and POST endpoint and will also use WebSockets. These instructions explain how to add these elements to your bot if you are upgrading or did not make your bot from a template. - -Open **BotController.cs** under the Controllers folder in your solution - -Find the `PostAsync` method in the class and update its decoration from [HttpPost] to [HttpPost, HttpGet]: - -```cs - -[HttpPost, HttpGet] -public async Task PostAsync() -{ - await _adapter.ProcessAsync(Request, Response, _bot); -} -``` - -Save and close BotController.cs - -Open **Startup.cs** in the root of your solution. - -In Startup.cs, navigate to the bottom of the Configure method. Before the call to `app.UseMvc()`, add a call to `app.UseWebSockets()`. This is important as the order of these use calls matters. The end of the method should look something like this: - -```cs -public void Configure(IApplicationBuilder app, IHostingEnvironment env) -{ - ... - app.UseDefaultFiles(); - app.UseStaticFiles(); - app.UseWebSockets(); - app.UseMvc(); - ... -} - -``` -The remainder of your bot code stays the same! - - - -Step 4: Optionally set the Speak field on Activities to customize what is spoken to the user - -By default, all messages sent through Direct Line Speech to the user will be spoken. - -You can optionally customize how the message is spoken by setting the Speak field of any Activity sent from the bot: - -```cs - -public IActivity Speak(string message) -{ - var activity = MessageFactory.Text(message); - string body = @" - - " + - $"{message}" + ""; - - activity.Speak = body; - return activity; -} -``` - -The following snippet shows how to use the previous Speak function: - -```cs - -protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) -{ - await turnContext.SendActivityAsync(Speak($"Echo: {turnContext.Activity.Text}"), cancellationToken); -} -``` - -## Additional information - -- For a complete example of creating and using a voice enabled bot, see [Tutorial: Voice-enable your bot using the Speech SDK](https://docs.microsoft.com/azure/cognitive-services/speech-service/tutorial-voice-enable-your-bot-speech-sdk). - -- For more information on working with activities, see [how bots work](https://docs.microsoft.com/azure/bot-service/bot-builder-basics) ans [how to send and receive text messageshttps://docs.microsoft.com/azure/bot-service/bot-builder-howto-send-messages?view=azure-bot-service-4.0](). - - diff --git a/articles/dl-network-isolation-concept.md b/articles/dl-network-isolation-concept.md new file mode 100644 index 000000000..27010c341 --- /dev/null +++ b/articles/dl-network-isolation-concept.md @@ -0,0 +1,71 @@ +--- +title: About network isolation in Azure AI Bot Service +description: Learn about Azure Virtual Network and how a virtual network lets you restrict user access to your bot. +displayName: private network, isolated network +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: concept-article +ms.custom: + - evergreen +--- + +# Network isolation in Azure AI Bot Service + +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** + +This article covers concepts around network isolation for your Azure bot and its dependent services. + +You may want to restrict access to your bot to a private network. +The only way to do this in the Azure AI Bot Service is to use the Direct Line App Service extension. +For example, you can use the App Service extension to host a company-internal bot and require users to access the bot from within your company network. + +For detailed instructions on how to configure your bot in a private network, see how to [Use an isolated network](./dl-network-isolation-how-to.md). + +For more information about the features that support network isolation, see: + +| Feature | Article | +|:-----------------------------------------|:-----------------------------------------------------------------------------------------------| +| Direct Line App Service extension | [Direct Line App Service extension](./bot-service-channel-directline-extension.md) | +| Azure Virtual Network | [What is Azure Virtual Network?](/azure/virtual-network/virtual-networks-overview) | +| Azure network security groups | [Network security groups](/azure/virtual-network/network-security-groups-overview) | +| Azure Private Link and private endpoints | [What is a private endpoint?](/azure/private-link/private-endpoint-overview) | +| Azure DNS | [Create an Azure DNS zone and record using the Azure portal](/azure/dns/dns-getstarted-portal) | + +## Use of private endpoints + +When your bot endpoint is within a virtual network, and with the appropriate rules set in your network security group, you can restrict access to both inbound and outbound requests for your bot's app service by using a private endpoint. + +Private endpoints are available in the Bot Service via the Direct Line App Service extension. See the requirements for using private endpoints below: + +1. Activities must be sent to and from the App Service endpoint. + + The App Service extension is co-located with your bot endpoint app service. All messages to and from the endpoint are local to your virtual network and reach your client directly without being sent to Bot Framework services. + +1. For [user authentication](./v4sdk/bot-builder-concept-authentication.md) to work, your bot client needs to communicate with the service provider—such as Microsoft Entra ID or GitHub—and the token endpoint. + + If your bot client is in your virtual network, you'll need to allowlist both endpoints from within your virtual network. Do this for the token endpoint via [service tags](./bot-service-channel-directline-extension-vnet.md). Your bot endpoint itself also needs access to the token endpoint, as described below. + +1. With the App Service extension, your bot endpoint and the App Service extension need to send outbound HTTPS requests to Bot Framework services. + + These requests are for various meta operations, such as retrieving your bot configuration or retrieving tokens from the token endpoint. To facilitate these requests, you need to setup and configure a private endpoint. + +## How the Bot Service implements private endpoints + +There are two main scenarios where private endpoints are used: + +- For your bot to access the token endpoint. +- For the Direct Line channel extension to access the Bot Service. + +A private endpoint _projects_ required services into your virtual network, so that they're available inside your network directly, without exposing your virtual network to the internet or allow-listing any IP addresses. All traffic through a private endpoint goes through the Azure internal servers to ensure that your traffic isn't leaked to the internet. + +The service uses two sub-resources, `Bot` and `Token`, to project services into your network. When you add a private endpoint, Azure generates a bot-specific DNS record for each sub-resource and configures the endpoint in the DNS zone group. This ensures that endpoints from different bots which target the same sub-resource can be distinguished from each other, while reusing the same DNS zone group resource. + +## Example Scenario + +Say you have a bot named **SampleBot** and a corresponding app service for it, `SampleBot.azurewebsites.net`, that serves as the messaging endpoint for this bot. +You configure a private endpoint for **SampleBot** with sub-resource type `Bot` in the Azure portal for public cloud, which creates a DNS zone group with an `A` record corresponding to `SampleBot.botplinks.botframework.com`. This DNS record maps to a local IP in your virtual network. Similarly, using the sub-resource type `Token` generates an endpoint, `SampleBot.bottoken.botframework.com`. + +The `A` record in the DNS zone you created is mapped to an IP Address within your virtual network. So, requests sent to this endpoint are local to your network and don't violate rules in your network security group or Azure firewall that restrict outbound traffic from your network. The Azure networking layer and Bot Framework services ensure that your requests are not leaked to the public internet, and isolation is maintained for your network. diff --git a/articles/dl-network-isolation-how-to.md b/articles/dl-network-isolation-how-to.md new file mode 100644 index 000000000..d02f1403d --- /dev/null +++ b/articles/dl-network-isolation-how-to.md @@ -0,0 +1,169 @@ +--- +title: Configure network isolation +description: Learn how to configure your bot in a virtual network to restrict user access to your bot. +displayName: private network, isolated network +author: JonathanFingold +ms.author: jameslew +manager: iawilt +ms.reviewer: yiba +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen +--- + +# Configure network isolation + +**Commencing September 1, 2023, it is strongly advised to employ the [Azure Service Tag](/azure/virtual-network/service-tags-overview#available-service-tags) method for network isolation. The utilization of DL-ASE should be limited to highly specific scenarios. Prior to implementing this solution in a production environment, we kindly recommend consulting your support team for guidance.** + +You can add network isolation to an existing Direct Line App Service extension bot. +A private endpoint lets your network isolated bot communicate with required Bot Framework services so that the bot can run correctly while being limited to the virtual network. + +To add network isolation to your bot: + +1. Use a virtual network and configure the network to prevent outbound traffic. At this point, your bot will lose the ability to communicate with other Bot Framework services. +1. Configure private endpoints to restore connectivity. +1. Restart you app service and test your bot within your isolated network. +1. Disable public network access to your bot. + +## Prerequisites + +- An Azure account. If you don't already have one, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. + - A subscription with permission to create Azure Virtual Network and network security group resources. +- A working Direct Line App Service extension bot. + - Your bot uses the Bot Framework SDK for C# or JavaScript, version 4.16 or later. + - Your bot has named pipes enabled. + - Your bot's app service has the Direct Line App Service extension enabled. +- A Web Chat control connected to your bot's Direct Line client. + +To confirm that your existing bot is configured correctly: + +1. In a browser, open the Direct Line client endpoint for your bot. For example, `https://.azurewebsites.net/.bot`. +1. Verify the page displays the following: + + ```json + {"v":"123","k":true,"ib":true,"ob":true,"initialized":true} + ``` + + - **v** shows the build version of the Direct Line App Service extension. + - **k** indicates whether the extension was able to read an extension key from its configuration. + - **initialized** indicates whether the extension was able to download bot metadata from Azure AI Bot Service. + - **ib** indicates whether the extension was able to establish an inbound connection to the bot. + - **ob** indicates whether the extension was able to establish an outbound connection from the bot. + +## Create a virtual network + +1. Go to [Azure portal](https://portal.azure.com). +1. Create an Azure Virtual Network resource in the same region as your bot. + - This creates both a virtual network and a subnet. + - Don't create any virtual machines. + - For general instructions, see [Create a virtual network using the Azure portal](/azure/virtual-network/quick-create-portal). +1. Open the app service resource for your bot and enable virtual network integration. + - Use the virtual network and subnet from the previous step. + - For general instructions, see [Enable virtual network integration in Azure App Service](/azure/app-service/configure-vnet-integration-enable). +1. Create a second subnet. You'll use the second subnet later to add your private endpoint. + +### Deny outbound traffic from your network + +1. Open the network security group associated with your first subnet. + - If no security group is configured, create one. For more information, see [Network security groups](/azure/virtual-network/network-security-groups-overview). +1. Under **Settings**, select **Outbound security rules**. + 1. In the outbound security rules list, enable **DenyAllInternetOutbound**. +1. Go to the app service resource for your bot. +1. Restart your app service. + +### Verify that connectivity is broken + +1. In a separate browser tab, open the Direct Line client endpoint for your bot. For example, `https://.azurewebsites.net/.bot`. +1. Verify the page displays the following: + + ```json + {"v":"123","k":true,"ib":true,"ob":true,"initialized":false} + ``` + + The value of `initialized` should be `false`, because your app service and app service extension are unable to connect to other Bot Framework services to initialize itself. Your bot is now isolated in a virtual network for outbound connections. + +## Create your private endpoint + +1. Go to [Azure portal](https://portal.azure.com). +1. Open the Azure Bot resource for your bot. +1. Under **Settings**, select **Networking**. + 1. On the **Private access** tab and select **Create a private endpoint**. + 1. On the **Resource** tab, for **Target sub-resource**, select **Bot** from the list. + 1. On the **Virtual Network** tab, select your virtual network and the second subnet you created. + 1. Save your private endpoint. + +### Add your private endpoint to your bot's app service + +1. Open the Azure App Service resource for your bot. +1. Under **Settings**, select **Configuration**. + 1. On the **Application settings** tab, select **New application setting**. + 1. Set **Name** to `DirectLineExtensionABSEndpoint`. + 1. Set **Value** to the private endpoint URL, for example, `https://.privatelink.directline.botframework.com/v3/extension`. + 1. Save the new setting. + +## Restart your app service and verify that connectivity is restored + +1. Restart the app service for your bot. +1. In a separate browser tab, open the Direct Line client endpoint for your bot. For example, `https://.azurewebsites.net/.bot`. +1. Verify the page displays the following: + + ```json + {"v":"123","k":true,"ib":true,"ob":true,"initialized":true} + ``` + + The value of `initialized` should be `true`. + +1. Use the Web Chat control connected to your bot's Direct Line client to interact with your bot inside the private network. + +If your private endpoint doesn't work correctly, you can add a rule to allow outbound traffic specifically to Azure AI Bot Service. + +> [!NOTE] +> This will make you virtual network a little less isolated. + +1. Open the network security group associated with your first subnet. +1. Under **Settings**, select **Outbound security rules**. + 1. In the outbound security rules list, enable **AllowAzureBotService**. +1. Go to the app service resource for your bot. +1. Restart your app service. + +## Disable public network access to your bot + +You can block public access to your Azure AI Bot Service and only allow access through Private Endpoint. You can disable network access of Azure AI Bot Service in Azure portal. + +> [!TIP] +> This will unconfigure the Teams channels. No other channels (except Direct Line) can be configurated or updated in Azure portal. + +1. Go to [Azure portal](https://portal.azure.com). +1. Open the app service for your bot. +1. Disable public network access. + +## Additional information + +### Virtual network configuration + +You have a couple options to configure your bot for a virtual network. + +- Create a virtual network and then enable Azure App Service within the network. This is the option used in this article. +- Create an App Service environment and then add an App Service Plan within the environment. + +### [Virtual network](#tab/network) + +1. Create a virtual network. +1. Enable Azure App Service integration within the virtual network. + +These are the steps used in this article, as described in the [Create a virtual network](#create-a-virtual-network) section. + +For more information, see [Create a virtual network using the Azure portal](/azure/virtual-network/quick-create-portal) and [Enable virtual network integration in Azure App Service](/azure/app-service/configure-vnet-integration-enable). + +### [App Service Environment](#tab/environment) + +The Direct Line App Service extension is available on all Azure App services, including those hosted within an Azure App Service Environment. An Azure App Service Environment provides isolation and is a good way to work within a virtual network. + +1. Create an internal or external App Service Environment. For more information, see [Create an External App Service Environment](/azure/app-service/environment/create-external-ase) and [Create and use an Internal Load Balancer App Service Environment](/azure/app-service/environment/create-ilb-ase). +1. Add an App Service Plan inside your environment. You can deploy your bots—such as a Direct Line App Service extension bot—within your plan. + 1. In the [Azure portal](https://portal.azure.com), create a new App Service Plan resource. + 1. Under **Region**, select your App Service Environment. + 1. Finish creating your App Service Plan. + +--- diff --git a/articles/docfx.json b/articles/docfx.json index 82972f91a..033527dae 100644 --- a/articles/docfx.json +++ b/articles/docfx.json @@ -2,9 +2,6 @@ "build": { "content": [ { - "group": "bot-service", - "src": ".", - "dest": ".", "files": [ "**/*.md", "**/*.yml" @@ -20,15 +17,16 @@ "LICENSE-CODE", "ThirdPartyNotices", "v4sdk/**" - ] + ], + "src": ".", + "dest": ".", + "group": "bot-service" }, { - "group": "bot-service", "files": [ "**/*.md", "**/*.yml" ], - "src": "v4sdk", "exclude": [ "**/obj/**", "**/includes/**", @@ -36,10 +34,10 @@ "LICENSE", "LICENSE-CODE", "ThirdPartyNotices" - ] + ], + "src": "v4sdk", + "group": "bot-service" } - - ], "resource": [ { @@ -55,9 +53,6 @@ ] }, { - "group": "bot-service", - "src": ".", - "dest": ".", "files": [ "**/*.png", "**/*.jpg", @@ -68,11 +63,12 @@ "**/obj/**", "**/includes/**", "v4sdk" - ] + ], + "src": ".", + "dest": ".", + "group": "bot-service" }, { - "group": "bot-service", - "src": "v4sdk", "files": [ "**/*.png", "**/*.jpg", @@ -82,25 +78,26 @@ "exclude": [ "**/obj/**", "**/includes/**" - ] + ], + "src": "v4sdk", + "group": "bot-service" } - ], "overwrite": [], "externalReference": [], "globalMetadata": { - "uhfHeaderId": "MSDocsHeader-BotFramework", - "breadcrumb_path": "/bot-framework/breadcrumb/TOC.json", + "uhfHeaderId": "azure", + "archive_url": "https://docs.microsoft.com/previous-versions/azure/bot-service/index-bf-sdk", + "breadcrumb_path": "/azure/bot-service/breadcrumb/TOC.json", "author": "kaiqb", "brand": "azure", "searchScope": [ "Azure", "BotService" ], - "ms.date": "12/13/2018", + "ms.date": "10/09/2024", "titleSuffix": "Bot Service", - "feedback_system": "GitHub", - "feedback_github_repo": "MicrosoftDocs/bot-docs" + "feedback_system": "None" }, "fileMetadata": { "ms.author": { @@ -110,10 +107,15 @@ "**/**.md": "kaiqb" }, "ms.date": { - "**/**.md": "12/13/2018" + "**/**.md": "10/09/2024" }, "monikerRange": { "v4sdk/**.md": "azure-bot-service-4.0" + }, + "exclude_monikers": { + "**/*.md": [ + "azure-bot-service-3.0" + ] } }, "template": [], @@ -122,9 +124,8 @@ "groups": { "bot-service": { "dest": "bot-service", - "moniker_range": ">= azure-bot-service-3.0" + "moniker_range": ">= azure-bot-service-4.0" } } - } } diff --git a/articles/dotnet/TOC.md b/articles/dotnet/TOC.md deleted file mode 100644 index ad88a0b43..000000000 --- a/articles/dotnet/TOC.md +++ /dev/null @@ -1,45 +0,0 @@ -# [Bot Framework SDK for .NET](bot-builder-dotnet-overview.md) -# [Key concepts](bot-builder-dotnet-concepts.md) -# Messages and activities -## [Activities overview](bot-builder-dotnet-activities.md) -## [Create messages](bot-builder-dotnet-create-messages.md) -## [Add media attachments to messages](bot-builder-dotnet-add-media-attachments.md) -## [Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md) - -## [Add speech to messages](bot-builder-dotnet-text-to-speech.md) -## [Add input hints to messages](bot-builder-dotnet-add-input-hints.md) -## [Add suggested actions to messages](bot-builder-dotnet-add-suggested-actions.md) -## [Send and receive activities](bot-builder-dotnet-connector.md) -## [Implement global message handlers](bot-builder-dotnet-global-handlers.md) -## [Intercept messages](bot-builder-dotnet-middleware.md) -## [Send proactive messages](bot-builder-dotnet-proactive-messages.md) -# Dialogs -## [Dialogs overview](bot-builder-dotnet-dialogs.md) -## [Manage conversation flow](bot-builder-dotnet-manage-conversation-flow.md) -## [Scorable dialogs](bot-builder-dotnet-scorable-dialogs.md) -# FormFlow -## [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -## [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) -## [FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -## [Pattern language](bot-builder-dotnet-formflow-pattern-language.md) -## [Localization](bot-builder-dotnet-formflow-localize.md) -## [JSON schema](bot-builder-dotnet-formflow-json-schema.md) -# Channels -## [Implement channel-specific functionality](bot-builder-dotnet-channeldata.md) -## [Build a Cortana skill](bot-builder-dotnet-cortana-skill.md) -## [Conduct audio calls with Skype](bot-builder-dotnet-audio-calls.md) - -# State data -## [Manage state data](bot-builder-dotnet-state.md) -## [Manage state data using Cosmos DB](bot-builder-dotnet-state-azure-cosmosdb.md) -## [Manage state data using Table storage](bot-builder-dotnet-state-azure-table-storage.md) -# [Recognize intent with LUIS](bot-builder-dotnet-luis-dialogs.md) -# [Request payment](bot-builder-dotnet-request-payment.md) -# [Add Azure Search](bot-builder-dotnet-search-azure.md) -# [Secure your bot](bot-builder-dotnet-security.md) diff --git a/articles/dotnet/bot-builder-dotnet-activities.md b/articles/dotnet/bot-builder-dotnet-activities.md deleted file mode 100644 index 2fff5f817..000000000 --- a/articles/dotnet/bot-builder-dotnet-activities.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Activities overview - Bot Service -description: Learn about the different activity types available within the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Activities overview - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[!INCLUDE [Activity concept overview](../includes/snippet-dotnet-concept-activity.md)] - -## Activity types in the Bot Framework SDK for .NET - -The following activity types are supported by the Bot Framework SDK for .NET. - -| Activity.Type | Interface | Description | -|------|------|------| -| [message](#message) | IMessageActivity | Represents a communication between bot and user. | -| [conversationUpdate](#conversationupdate) | IConversationUpdateActivity | Indicates that the bot was added to a conversation, other members were added to or removed from the conversation, or conversation metadata has changed. | -| [contactRelationUpdate](#contactrelationupdate) | IContactRelationUpdateActivity | Indicates that the bot was added or removed from a user's contact list. | -| [typing](#typing) | ITypingActivity | Indicates that the user or bot on the other end of the conversation is compiling a response. | -| [deleteUserData](#deleteuserdata) | n/a | Indicates to a bot that a user has requested that the bot delete any user data it may have stored. | -| [endOfConversation](#endofconversation) | IEndOfConversationActivity | Indicates the end of a conversation. | -| [event](#event) | IEventActivity | Represents a communication sent to a bot that is not visible to the user. | -| [invoke](#invoke) | IInvokeActivity | Represents a communication sent to a bot to request that it perform a specific operation. This activity type is reserved for internal use by the Microsoft Bot Framework. | -| [messageReaction](#messagereaction) | IMessageReactionActivity | Indicates that a user has reacted to an existing activity. For example, a user clicks the "Like" button on a message. | - -## message - -Your bot will send **message** activities to communicate information to and receive **message** activities from users. -Some messages may simply consist of plain text, while others may contain richer content such as [text to be spoken](bot-builder-dotnet-text-to-speech.md), [suggested actions](bot-builder-dotnet-add-suggested-actions.md), [media attachments](bot-builder-dotnet-add-media-attachments.md), [rich cards](bot-builder-dotnet-add-rich-card-attachments.md), and [channel-specific data](bot-builder-dotnet-channeldata.md). -For information about commonly-used message properties, see [Create messages](bot-builder-dotnet-create-messages.md). - -## conversationUpdate - -A bot receives a **conversationUpdate** activity whenever it has been added to a conversation, -other members have been added to or removed from a conversation, -or conversation metadata has changed. - -If members have been added to the conversation, the activity's `MembersAdded` property will contain an array of -`ChannelAccount` objects to identify the new members. - -To determine whether your bot has been added to the conversation (i.e., is one of the new members), evaluate whether the `Recipient.Id` value for the activity (i.e., your bot's id) -matches the `Id` property for any of the accounts in the `MembersAdded` array. - -If members have been removed from the conversation, the `MembersRemoved` property will contain an array of `ChannelAccount` objects to identify the removed members. - -> [!TIP] -> If your bot receives a **conversationUpdate** activity indicating that a user has joined the conversation, -> you may choose to have it respond by sending a welcome message to that user. - -## contactRelationUpdate - -A bot receives a **contactRelationUpdate** activity whenever it is added to or removed from a user's contact list. The value of the activity's `Action` property (add | remove) indicates whether the bot has been added or removed from the user's contact list. - -## typing - -A bot receives a **typing** activity to indicate that the user is typing a response. -A bot may send a **typing** activity to indicate to the user that it is working to fulfill a request or compile a response. - -## deleteUserData - -A bot receives a **deleteUserData** activity when a user requests deletion of any data that the bot has previously persisted for him or her. If your bot receives this type of activity, it should delete any personally identifiable information (PII) that it has previously stored for the user that made the request. - -## endOfConversation - -A bot receives an **endOfConversation** activity to indicate that the user has ended the conversation. A bot may send an **endOfConversation** activity to indicate to the user that the conversation is ending. - -## event - -Your bot may receive an **event** activity from an external process or service that wants to -communicate information to your bot without that information being visible to users. The -sender of an **event** activity typically does not expect the bot to acknowledge receipt in any way. - -## invoke - -Your bot may receive an **invoke** activity that represents a request for it to perform a specific operation. -The sender of an **invoke** activity typically expects the bot to acknowledge receipt via HTTP response. -This activity type is reserved for internal use by the Microsoft Bot Framework. - -## messageReaction - -Some channels will send **messageReaction** activities to your bot when a user reacted to an existing activity. For example, a user clicks the "Like" button on a message. The **ReplyToId** property will indicate which activity the user reacted to. - -The **messageReaction** activity may correspond to any number of **messageReactionTypes** that the channel defined. For example, "Like" or "PlusOne" as reaction types that a channel may send. - -## Additional resources - -- [Send and receive activities](bot-builder-dotnet-connector.md) -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Activity class](https://aka.ms/ActivityClass-dotnet-API) diff --git a/articles/dotnet/bot-builder-dotnet-add-input-hints.md b/articles/dotnet/bot-builder-dotnet-add-input-hints.md deleted file mode 100644 index 8bed49b97..000000000 --- a/articles/dotnet/bot-builder-dotnet-add-input-hints.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Add input hints to messages (v3 C#) - Bot Service -description: Learn how to add input hints to messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add input hints to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-input-hints.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-input-hints.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-input-hints.md) - -By specifying an input hint for a message, you can indicate whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. For many channels, this enables clients to set the state of user input controls accordingly. For example, if a message's input hint indicates that the bot is ignoring user input, the client may close the microphone and disable the input box to prevent the user from providing input. - -## Accepting input - -To indicate that your bot is passively ready for input but is not awaiting a response from the user, set the message's input hint to `InputHints.AcceptingInput`. On many channels, this will cause the client's input box to be enabled and microphone to be closed, but still accessible to the user. For example, Cortana will open the microphone to accept input from the user if the user holds down the microphone button. The following code example creates a message that indicates the bot is accepting user input. - -```cs -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -reply.InputHint = InputHints.AcceptingInput; -await connector.Conversations.ReplyToActivityAsync(reply); -``` - -## Expecting input - -To indicate that your bot is awaiting a response from the user, set the message's input hint to `InputHints.ExpectingInput`. On many channels, this will cause the client's input box to be enabled and microphone to be open. The following code example creates a message that indicates the bot is expecting user input. - -```cs -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -reply.InputHint = InputHints.ExpectingInput; -await connector.Conversations.ReplyToActivityAsync(reply); -``` - -## Ignoring input - -To indicate that your bot is not ready to receive input from the user, set the message's input hint to `InputHints.IgnorningInput`. On many channels, this will cause the client's input box to be disabled and microphone to be closed. The following code example creates a message that indicates the bot is ignoring user input. - -```cs -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -reply.InputHint = InputHints.IgnoringInput; -await connector.Conversations.ReplyToActivityAsync(reply); -``` - -## Default values for input hint - -If you do not set the input hint for a message, the Bot Framework SDK will automatically set it for you by using this logic: - -- If your bot sends a prompt, the input hint for the message will specify that your bot is **expecting input**. -- If your bot sends single message, the input hint for the message will specify that your bot is **accepting input**. -- If your bot sends a series of consecutive messages, the input hint for all but the final message in the series will specify that your bot is **ignoring input**, and the input hint for the final message in the series will specify that your bot is **accepting input**. - -## Additional resources - -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Add speech to messages](bot-builder-dotnet-text-to-speech.md) -- Activity class -- InputHints class diff --git a/articles/dotnet/bot-builder-dotnet-add-media-attachments.md b/articles/dotnet/bot-builder-dotnet-add-media-attachments.md deleted file mode 100644 index 43b0898e4..000000000 --- a/articles/dotnet/bot-builder-dotnet-add-media-attachments.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Add media attachments to messages (v3 C#) - Bot Service -description: Learn how to add media attachments to messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add media attachments to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-media-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-receive-attachments.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-media-attachments.md) - -A message exchange between user and bot can contain media attachments (e.g., image, video, audio, file). - -The `Attachments` property of the Activity object contains an array of Attachment objects that represent the media attachments and rich cards within to the message. - -> [!NOTE] -> [Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md). - -## Add a media attachment - -To add a media attachment to a message, create an `Attachment` object for the `message` activity and set -the `ContentType`, `ContentUrl`, and `Name` properties. - -[!code-csharp[Add media attachment](../includes/code/dotnet-add-attachments.cs#addMediaAttachment)] - -If an attachment is an image, audio, or video, the Connector service will communicate attachment data to the channel in a way that enables the [channel](bot-builder-dotnet-channeldata.md) to render that attachment within the conversation. If the attachment is a file, the file URL will be rendered as a hyperlink within the conversation. - -## Additional resources - -- [Channels reference][inspector] -- [Activities overview](bot-builder-dotnet-activities.md) -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md) -- Activity class -- Attachment class - -[inspector]: ../bot-service-channels-reference.md - diff --git a/articles/dotnet/bot-builder-dotnet-add-rich-card-attachments.md b/articles/dotnet/bot-builder-dotnet-add-rich-card-attachments.md deleted file mode 100644 index fdc25d631..000000000 --- a/articles/dotnet/bot-builder-dotnet-add-rich-card-attachments.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: Add rich card attachments to messages (v3 C#) - Bot Service -description: Learn how to add rich cards to messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add rich card attachments to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-rich-card-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-rich-cards.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-rich-cards.md) - -A message exchange between user and bot can contain one or more rich cards rendered as a list or carousel. - -The `Attachments` property of the [Activity](https://docs.botframework.com/csharp/builder/sdkreference/dc/d2f/class_microsoft_1_1_bot_1_1_connector_1_1_activity.html) object contains an array of [Attachment](https://docs.microsoft.com/dotnet/api/microsoft.bot.connector.attachments?view=botconnector-3.12.2.4) objects that represent the rich cards and media attachments within the message. - -> [!NOTE] -> For information about how to add media attachments to messages, see -> [Add media attachments to messages](bot-builder-dotnet-add-media-attachments.md). - -## Types of rich cards - -The Bot Framework currently supports eight types of rich cards: - -| Card type | Description | -|----|----| -| [Adaptive Card](/adaptive-cards/get-started/bots) | A customizable card that can contain any combination of text, speech, images, buttons, and input fields. See [per-channel support](/adaptive-cards/get-started/bots#channel-status). | -| [Animation Card][animationCard] | A card that can play animated GIFs or short videos. | -| [Audio Card][audioCard] | A card that can play an audio file. | -| [Hero Card][heroCard] | A card that typically contains a single large image, one or more buttons, and text. | -| [Thumbnail Card][thumbnailCard] | A card that typically contains a single thumbnail image, one or more buttons, and text. | -| [Receipt Card][receiptCard] | A card that enables a bot to provide a receipt to the user. It typically contains the list of items to include on the receipt, tax and total information, and other text. | -| [SignIn Card][signinCard] | A card that enables a bot to request that a user sign-in. It typically contains text and one or more buttons that the user can click to initiate the sign-in process. | -| [Video Card][videoCard] | A card that can play videos. | - -> [!TIP] -> To display multiple rich cards in list format, set the activity's `AttachmentLayout` property to "list". -> To display multiple rich cards in carousel format, set the activity's `AttachmentLayout` property to "carousel". -> If the channel does not support carousel format, it will display the rich cards in list format, even if the `AttachmentLayout` property specifies "carousel". - -## Process events within rich cards - -To process events within rich cards, define `CardAction` objects to specify what should happen when the user clicks a button or taps a section of the card. Each `CardAction` object contains these properties: - -| Property | Type | Description | -|----|----|----| -| Type | string | type of action (one of the values specified in the table below) | -| Title | string | title of the button | -| Image | string | image URL for the button | -| Value | string | value needed to perform the specified type of action | - -> [!NOTE] -> Buttons within Adaptive Cards are not created using `CardAction` objects, -> but instead using the schema that is defined by [Adaptive Cards](http://adaptivecards.io). -> See [Add an Adaptive Card to a message](#adaptive-card) for an example that shows how to -> add buttons to an Adaptive Card. - -This table lists the valid values for `CardAction.Type` and describes -the expected contents of `CardAction.Value` for each type: - -| CardAction.Type | CardAction.Value | -|----|----| -| openUrl | URL to be opened in the built-in browser | -| imBack | Text of the message to send to the bot (from the user who clicked the button or tapped the card). This message (from user to bot) will be visible to all conversation participants via the client application that is hosting the conversation. | -| postBack | Text of the message to send to the bot (from the user who clicked the button or tapped the card). Some client applications may display this text in the message feed, where it will be visible to all conversation participants. | -| call | Destination for a phone call in this format: **tel:123123123123** | -| playAudio | URL of audio to be played | -| playVideo | URL of video to be played | -| showImage | URL of image to be displayed | -| downloadFile | URL of file to be downloaded | -| signin | URL of OAuth flow to be initiated | - -## Add a Hero card to a message - -The Hero card typically contains a single large image, one or more buttons, and text. - -This code example shows how to create a reply message that contains three Hero cards rendered in carousel format: - -[!code-csharp[Add HeroCard attachment](../includes/code/dotnet-add-attachments.cs#addHeroCardAttachment)] - -## Add a Thumbnail card to a message - -The Thumbnail card typically contains a single thumbnail image, one or more buttons, and text. - -This code example shows how to create a reply message that contains two Thumbnail cards rendered in list format: - -[!code-csharp[Add ThumbnailCard attachment](../includes/code/dotnet-add-attachments.cs#addThumbnailCardAttachment)] - -## Add a Receipt card to a message - -The Receipt card enables a bot to provide a receipt to the user. -It typically contains the list of items to include on the receipt, tax and total information, and other text. - -This code example shows how to create a reply message that contains a Receipt card: - -[!code-csharp[Add ReceiptCard attachment](../includes/code/dotnet-add-attachments.cs#addReceiptCardAttachment)] - -## Add a Sign-in card to a message - -The Sign-in card enables a bot to request that a user sign-in. -It typically contains text and one or more buttons that the user can click to initiate the sign-in process. - -This code example shows how to create a reply message that contains a Sign-in card: - -[!code-csharp[Add SignInCard attachment](../includes/code/dotnet-add-attachments.cs#addSignInCardAttachment)] - -## Add an Adaptive card to a message - -The Adaptive Card can contain any combination of text, speech, images, buttons, and input fields. -Adaptive Cards are created using the JSON format specified in Adaptive Cards, which gives you full control over card content and format. - -To create an Adaptive Card using .NET, install the `AdaptiveCards` NuGet package. Then, leverage the information within the Adaptive Cards site to understand Adaptive Card schema, explore Adaptive Card elements, and see JSON samples that can be used to create cards of varying composition and complexity. Additionally, you can use the Interactive Visualizer to design Adaptive Card payloads and preview card output. - -This code example shows how to create a message that contains an Adaptive Card for a calendar reminder: - -[!code-csharp[Add Adaptive Card attachment](../includes/code/dotnet-add-attachments.cs#addAdaptiveCardAttachment)] - -The resulting card contains three blocks of text, an input field (choice list), and three buttons: - -![Adaptive Card calendar reminder](../media/adaptive-card-reminder.png) - -## Additional resources - -- [Channels reference](../bot-service-channels-reference.md) -- Adaptive Cards -- [Activities overview](bot-builder-dotnet-activities.md) -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Add media attachments to messages](bot-builder-dotnet-add-media-attachments.md) -- Activity class -- Attachment class - -[animationCard]: /dotnet/api/microsoft.bot.connector.animationcard -[audioCard]: /dotnet/api/microsoft.bot.connector.audiocard -[heroCard]: /dotnet/api/microsoft.bot.connector.herocard -[thumbnailCard]: /dotnet/api/microsoft.bot.connector.thumbnailcard -[receiptCard]: /dotnet/api/microsoft.bot.connector.receiptcard -[signinCard]: /dotnet/api/microsoft.bot.connector.signincard -[videoCard]: /dotnet/api/microsoft.bot.connector.videocard - -[inspector]: ../bot-service-channels-reference.md diff --git a/articles/dotnet/bot-builder-dotnet-add-suggested-actions.md b/articles/dotnet/bot-builder-dotnet-add-suggested-actions.md deleted file mode 100644 index 871958668..000000000 --- a/articles/dotnet/bot-builder-dotnet-add-suggested-actions.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Add suggested actions to messages (v3 C#) - Bot Service -description: Learn how to add suggested actions to messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 03/13/2018 -monikerRange: 'azure-bot-service-3.0' ---- -# Add suggested actions to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-suggested-actions.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-suggested-actions.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-suggested-actions.md) - -[!INCLUDE [Introduction to suggested actions](../includes/snippet-suggested-actions-intro.md)] - -## Send suggested actions - -To add suggested actions to a message, set the `SuggestedActions` property of the activity to a list of [CardAction][cardAction] objects that represent the buttons to be presented to the user. - -This code example shows how to create a message that presents three suggested actions to the user: - -[!code-csharp[Add suggested actions](../includes/code/dotnet-add-suggested-actions.cs#addSuggestedActions)] - -When the user taps one of the suggested actions, the bot will receive a message from the user that contains the `Value` of the corresponding action. - -## Additional resources - -- [Activities overview](bot-builder-dotnet-activities.md) -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Activity class](https://aka.ms/ActivityClass-dotnet-API) -- IMessageActivity interface -- CardAction class -- SuggestedActions class - -[cardAction]: /dotnet/api/microsoft.bot.connector.cardaction - -[inspector]: ../bot-service-channel-inspector.md - - diff --git a/articles/dotnet/bot-builder-dotnet-audio-calls.md b/articles/dotnet/bot-builder-dotnet-audio-calls.md deleted file mode 100644 index fd9e9b02b..000000000 --- a/articles/dotnet/bot-builder-dotnet-audio-calls.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Conduct audio calls with Skype - Bot Service -description: Learn how to conduct audio calls with Skype using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Conduct audio calls with Skype - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[!INCLUDE [Introduction to conducting audio calls](../includes/snippet-audio-call-intro.md)] - -The architecture for a bot that supports audio calls is very similar to that of a typical bot. -The following code samples show how to enable support for audio calls via Skype with the Bot Framework SDK for .NET. - -## Enable support for audio calls - -To enable a bot to support audio calls, define the `CallingController`. - -```cs -[BotAuthentication] -[RoutePrefix("api/calling")] -public class CallingController : ApiController -{ - public CallingController() : base() - { - CallingConversation.RegisterCallingBot(callingBotService => new IVRBot(callingBotService)); - } - - [Route("callback")] - public async Task ProcessCallingEventAsync() - { - return await CallingConversation.SendAsync(this.Request, CallRequestType.CallingEvent); - } - - [Route("call")] - public async Task ProcessIncomingCallAsync() - { - return await CallingConversation.SendAsync(this.Request, CallRequestType.IncomingCall); - } -} -``` - -> [!NOTE] -> In addition to the `CallingController`, which supports audio calls, a bot may also contain a -> `MessagesController` to support messages. Providing both options allows users to interact with -> the bot in the way that they prefer. - -## Answer the call - -The `ProcessIncomingCallAsync` task will execute whenever a user initiates a call to this bot from Skype. -The constructor registers the `IVRBot` class, which has a predefined handler for the `incomingCallEvent`. - -The first action within the workflow should determine if the bot answers or rejects the incoming call. This workflow instructs the bot to answer the incoming call and then play a welcome message. - -```cs -private Task OnIncomingCallReceived(IncomingCallEvent incomingCallEvent) -{ - this.callStateMap[incomingCallEvent.IncomingCall.Id] = new CallState(incomingCallEvent.IncomingCall.Participants); - - incomingCallEvent.ResultingWorkflow.Actions = new List - { - new Answer { OperationId = Guid.NewGuid().ToString() }, - GetPromptForText(WelcomeMessage) - }; - - return Task.FromResult(true); -} -``` - -## After the bot answers - -If the bot answers the call, subsequent actions specified within the workflow will instruct the -**Skype Bot Platform for Calling** to play prompt, record audio, recognize speech, or collect digits from a dial pad. -The final action of the workflow might be to end the call. - -This code sample defines a handler that will set up a menu after the welcome message completes. - -```cs -private Task OnPlayPromptCompleted(PlayPromptOutcomeEvent playPromptOutcomeEvent) -{ - var callState = this.callStateMap[playPromptOutcomeEvent.ConversationResult.Id]; - SetupInitialMenu(playPromptOutcomeEvent.ResultingWorkflow); - return Task.FromResult(true); -} -``` - -The `CreateIvrOptions` method defines that menu that will be presented to the user. - -```cs -private static Recognize CreateIvrOptions(string textToBeRead, int numberOfOptions, bool includeBack) -{ - if (numberOfOptions > 9) - { - throw new Exception("too many options specified"); - } - - var choices = new List(); - - for (int i = 1; i <= numberOfOptions; i++) - { - choices.Add(new RecognitionOption { Name = Convert.ToString(i), DtmfVariation = (char)('0' + i) }); - } - - if (includeBack) - { - choices.Add(new RecognitionOption { Name = "#", DtmfVariation = '#' }); - } - - var recognize = new Recognize - { - OperationId = Guid.NewGuid().ToString(), - PlayPrompt = GetPromptForText(textToBeRead), - BargeInAllowed = true, - Choices = choices - }; - - return recognize; -} -``` - -The `RecognitionOption` class defines both the spoken answer as well as the corresponding Dual-Tone Multi-Frequency (DTMF) variation. DTMF enables the user to answer by typing the corresponding digits on the keypad instead of speaking. - -The `OnRecognizeCompleted` method processes the user's selection, and the input parameter `recognizeOutcomeEvent` contains the value of the user's selection. - -```cs -private Task OnRecognizeCompleted(RecognizeOutcomeEvent recognizeOutcomeEvent) -{ - var callState = this.callStateMap[recognizeOutcomeEvent.ConversationResult.Id]; - ProcessMainMenuSelection(recognizeOutcomeEvent, callState); - return Task.FromResult(true); -} -``` - -## Support natural language -The bot can also be designed to support natural language responses. The **Bing Speech API** enables the bot to recognize words in the user's spoken reply. - -```cs -private async Task OnRecordCompleted(RecordOutcomeEvent recordOutcomeEvent) -{ - recordOutcomeEvent.ResultingWorkflow.Actions = new List - { - GetPromptForText(EndingMessage), - new Hangup { OperationId = Guid.NewGuid().ToString() } - }; - - // Convert the audio to text - if (recordOutcomeEvent.RecordOutcome.Outcome == Outcome.Success) - { - var record = await recordOutcomeEvent.RecordedContent; - string text = await this.GetTextFromAudioAsync(record); - - var callState = this.callStateMap[recordOutcomeEvent.ConversationResult.Id]; - - await this.SendSTTResultToUser("We detected the following audio: " + text, callState.Participants); - } - - recordOutcomeEvent.ResultingWorkflow.Links = null; - this.callStateMap.Remove(recordOutcomeEvent.ConversationResult.Id); -} -``` - -## Sample code - -For a complete sample that shows how to support audio calls with Skype using the Bot Framework SDK for .NET, see the Skype Calling Bot sample in GitHub. - -## Additional resources - -- Bot Framework SDK for .NET Reference -- Skype Calling Bot sample (GitHub) diff --git a/articles/dotnet/bot-builder-dotnet-channeldata.md b/articles/dotnet/bot-builder-dotnet-channeldata.md deleted file mode 100644 index cab732f8f..000000000 --- a/articles/dotnet/bot-builder-dotnet-channeldata.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Implement channel-specific functionality (v3 C#) - Bot Service -description: Learn how to implement channel-specific functionality using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Implement channel-specific functionality - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[!INCLUDE [Channel Data Content](../includes/snippet-channeldata.md)] diff --git a/articles/dotnet/bot-builder-dotnet-concepts.md b/articles/dotnet/bot-builder-dotnet-concepts.md deleted file mode 100644 index 29d8f0d90..000000000 --- a/articles/dotnet/bot-builder-dotnet-concepts.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Key concepts in the Bot Framework SDK for .NET - Bot Service -description: Understand the key concepts and tools for building and deploying conversational bots available in the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Key concepts in the Bot Framework SDK for .NET - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-concepts.md) -> - [Node.js](../nodejs/bot-builder-nodejs-concepts.md) - -This article introduces key concepts in the Bot Framework SDK for .NET. - -## Connector - -The [Bot Framework Connector](bot-builder-dotnet-connector.md) provides a single REST API that enables a bot to communicate across multiple channels such as Skype, Email, Slack, and more. It facilitates communication between bot and user by relaying messages from bot to channel and from channel to bot. - -In the Bot Framework SDK for .NET, the [Connector][connectorLibrary] library enables access to the Connector. - -## Activity - -[!INCLUDE [Activity concept overview](../includes/snippet-dotnet-concept-activity.md)] - -For details about Activities in the Bot Framework SDK for .NET, -see [Activities overview](bot-builder-dotnet-activities.md). - -## Dialog - -When you create a bot using the Bot Framework SDK for .NET, you can use [dialogs](bot-builder-dotnet-dialogs.md) to model -a conversation and manage [conversation flow](../bot-service-design-conversation-flow.md#dialog-stack). -A dialog can be composed of other dialogs to maximize reuse, and a dialog context maintains the [stack of dialogs](../bot-service-design-conversation-flow.md) that are active in the conversation at any point in time. -A conversation that comprises dialogs is portable across computers, which makes it possible for your bot implementation to scale. - -In the Bot Framework SDK for .NET, the [Builder][builderLibrary] library enables you to manage dialogs. - -## FormFlow - -You can use [FormFlow](bot-builder-dotnet-formflow.md) within the Bot Framework SDK for .NET to streamline of building a bot that collects information from the user. -For example, a bot that takes sandwich orders must collect several pieces of information from the user such as type of bread, choice of toppings, size, and so on. Given basic guidelines, FormFlow can automatically generate the dialogs necessary to manage a guided conversation like this. - -## State - -[!INCLUDE [State concept overview](../includes/snippet-dotnet-concept-state.md)] - -For details about managing state using the Bot Framework SDK for .NET, -see [Manage state data](bot-builder-dotnet-state.md). - -## Naming conventions - -The Bot Framework SDK for .NET library uses strongly-typed, Pascal-cased naming conventions. -However, the JSON messages that are transported back and forth over the wire use camel-case naming conventions. -For example, the C# property **ReplyToId** is serialized as **replyToId** in the JSON message that's -transported over the wire. - -## Security - -You should ensure that your bot's endpoint can only be called by the Bot Framework Connector service. -For more information on this topic, see [Secure your bot](bot-builder-dotnet-security.md). - -## Next steps - -Now you know the concepts behind every bot. You can quickly [build a bot using Visual Studio](bot-builder-dotnet-quickstart.md) using a template. Next, study each key concept in more detail, starting with dialogs. - -> [!div class="nextstepaction"] -> [Dialogs in the Bot Framework SDK for .NET](bot-builder-dotnet-dialogs.md) - -[connectorLibrary]: /dotnet/api/microsoft.bot.connector - -[builderLibrary]: /dotnet/api/microsoft.bot.builder.dialogs diff --git a/articles/dotnet/bot-builder-dotnet-connector.md b/articles/dotnet/bot-builder-dotnet-connector.md deleted file mode 100644 index 99a900694..000000000 --- a/articles/dotnet/bot-builder-dotnet-connector.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Send and receive activities - Bot Service -description: Learn how to exchange information with a user across various channels by using the Connector service via the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Send and receive activities - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -The Bot Framework Connector provides a single REST API that enables a bot to communicate across multiple -channels such as Skype, Email, Slack, and more. -It facilitates communication between bot and user, by relaying messages from bot to channel -and from channel to bot. - -This article describes how to use the Connector via the Bot Framework SDK for .NET to -exchange information between bot and user on a channel. - -> [!NOTE] -> While it is possible to construct a bot by exclusively using the techniques that are described -> in this article, the Bot Framework SDK provides additional features like -> [dialogs](bot-builder-dotnet-dialogs.md) and [FormFlow](bot-builder-dotnet-formflow.md) that -> can streamline the process of managing conversation flow and state and -> make it simpler to incorporate cognitive services such as language understanding. - -## Create a connector client - -The [ConnectorClient][ConnectorClient] class contains the methods that a bot uses to communicate with a user on a channel. -When your bot receives an Activity object from the Connector, -it should use the `ServiceUrl` specified for that activity to create the connector client that it'll -subsequently use to generate a response. - -[!code-csharp[Create connector client](../includes/code/dotnet-send-and-receive.cs#createConnectorClient)] - -> [!TIP] -> Because a channel's endpoint may not be stable, your bot should direct communications to the endpoint -> that the Connector specifies in the `Activity` object, whenever possible (rather than relying upon a cached endpoint). -> -> If your bot needs to initiate the conversation, it can use a cached endpoint for the specified channel -> (since there will be no incoming `Activity` object in that scenario), but it should refresh cached endpoints often. - -## Create a reply - -The Connector uses an [Activity](bot-builder-dotnet-activities.md) object to pass information back and forth between bot and channel (user). -Every activity contains information used for routing the message to the appropriate destination -along with information about who created the message (`From` property), -the context of the message, and the recipient of the message (`Recipient` property). - -When your bot receives an activity from the Connector, the incoming activity's `Recipient` property specifies -the bot's identity in that conversation. -Because some channels (e.g., Slack) assign the bot a new identity when it's added to a conversation, -the bot should always use the value of the incoming activity's `Recipient` property as the value of -the `From` property in its response. - -Although you can create and initialize the outgoing `Activity` object yourself from scratch, -the Bot Framework SDK provides an easier way of creating a reply. -By using the incoming activity's `CreateReply` method, -you simply specify the message text for the response, and the outgoing activity is created -with the `Recipient`, `From`, and `Conversation` properties automatically populated. - -[!code-csharp[Create reply](../includes/code/dotnet-send-and-receive.cs#createReply)] - -## Send a reply - -Once you've created a reply, you can send it by calling the connector client's `ReplyToActivity` method. -The Connector will deliver the reply using the appropriate channel semantics. - -[!code-csharp[Send reply](../includes/code/dotnet-send-and-receive.cs#sendReply)] - -> [!TIP] -> If your bot is replying to a user's message, always use the `ReplyToActivity` method. - -## Send a (non-reply) message - -If your bot is part of a conversation, it can send a message that is not a direct reply to -any message from the user by calling the `SendToConversation` method. - -[!code-csharp[Send non-reply message](../includes/code/dotnet-send-and-receive.cs#sendNonReplyMessage)] - -You may use the `CreateReply` method to initialize the new message (which would automatically set -the `Recipient`, `From`, and `Conversation` properties for the message). -Alternatively, you could use the `CreateMessageActivity` method to create the new message -and set all property values yourself. - -> [!NOTE] -> The Bot Framework does not impose any restrictions on the number of messages that a bot may send. -> However, most channels enforce throttling limits to restrict bots from sending a large number of messages in a short period of time. -> Additionally, if the bot sends multiple messages in quick succession, -> the channel may not always render the messages in the proper sequence. - -## Start a conversation - -There may be times when your bot needs to initiate a conversation with one or more users. -You can start a conversation by calling either the `CreateDirectConversation` method (for a private conversation with a single user) -or the `CreateConversation` method (for a group conversation with multiple users) -to retrieve a `ConversationAccount` object. -Then, create the message and send it by calling the `SendToConversation` method. -To use either the `CreateDirectConversation` method or the `CreateConversation` method, -you must first [create the connector client](#create-a-connector-client) by using the target channel's service URL -(which you may retrieve from cache, if you've persisted it from previous messages). - -> [!NOTE] -> Not all channels support group conversations. -> To determine whether a channel supports group conversations, consult the channel's documentation. - -This code example uses the `CreateDirectConversation` method to create a private conversation with a single user. - -[!code-csharp[Start private conversation](../includes/code/dotnet-send-and-receive.cs#startPrivateConversation)] - -This code example uses the `CreateConversation` method to create a group conversation with multiple users. - -[!code-csharp[Start group conversation](../includes/code/dotnet-send-and-receive.cs#startGroupConversation)] - -## Additional resources - -- [Activities overview](bot-builder-dotnet-activities.md) -- [Create messages](bot-builder-dotnet-create-messages.md) -- Bot Framework SDK for .NET Reference -- Activity class -- ConnectorClient class - -[ConnectorClient]: /dotnet/api/microsoft.bot.connector.connectorclient diff --git a/articles/dotnet/bot-builder-dotnet-cortana-skill.md b/articles/dotnet/bot-builder-dotnet-cortana-skill.md deleted file mode 100644 index 4d584aba4..000000000 --- a/articles/dotnet/bot-builder-dotnet-cortana-skill.md +++ /dev/null @@ -1,364 +0,0 @@ ---- -title: Build a Cortana skill using .NET - Bot Service -description: Learn core concepts for building a Cortana skill in the Bot Framework SDK for .NET. -keywords: Bot Framework, Cortana skill, speech, .NET, SDK, key concepts, core concepts -author: DeniseMak -manager: kamrani -ms.author: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' - -#ROBOTS: Index ---- - -# Build a speech-enabled bot with Cortana skills - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-cortana-skill.md) -> - [Node.js](../nodejs/bot-builder-nodejs-cortana-skill.md) - - -The Bot Framework SDK for .NET enables you to a build speech-enabled bot by connecting it to the Cortana channel as a Cortana skill. - - -> [!TIP] -> For more information on what a skill is, and what they can do, see [The Cortana Skills Kit][CortanaGetStarted]. - -Creating a Cortana skill using Bot Framework requires very little Cortana-specific knowledge and primarily consists of building a bot. One of the likely key differences from other bots that you may have created in the past is that Cortana has both a visual and an audio component. For the visual component, Cortana provides an area of the canvas for rendering content such as cards. For the audio component, you provide text or SSML in your bot's messages, which Cortana reads to the user, giving your bot a voice. - -> [!NOTE] -> Cortana is available on many different devices. Some have a screen while others, like a standalone speaker, might not. You should make sure that your bot is capable of handling both scenarios. See [Cortana-specific entities][CortanaSpecificEntities] to learn how to check device information. - -## Adding speech to your bot - -Spoken messages from your bot are represented as Speech Synthesis Markup Language (SSML). The Bot Framework SDK lets you include SSML in your bot's responses to control what the bot says, in addition to what it shows. You can also control the state of Cortana's microphone, by specifying whether your bot is accepting, expecting, or ignoring user input. - -Set the `Speak` property of the `IMessageActivity` object to specify a message for Cortana to say. If you specify plain text, Cortana determines how the words are pronounced. - -```cs -Activity reply = activity.CreateReply("This is the text that Cortana displays."); -reply.Speak = "This is the text that Cortana will say."; -``` - -If you want more control over pitch, tone, and emphasis, format the `Speak` property as [Speech Synthesis Markup Language (SSML)](http://www.w3.org/TR/speech-synthesis/). - -The following code example specifies that the word "text" should be spoken with a moderate amount of emphasis: -```cs -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -``` - - -The **InputHint** property helps indicate to Cortana whether your bot is expecting input. The default value is **ExpectingInput** for a prompt, and **AcceptingInput** for other types of responses. - - -| Value | Description | -|------|------| -| **AcceptingInput** | Your bot is passively ready for input but is not waiting on a response. Cortana accepts input from the user if the user holds down the microphone button.| -| **ExpectingInput** | Indicates that the bot is actively expecting a response from the user. Cortana listens for the user to speak into the microphone. | -| **IgnoringInput** | Cortana is ignoring input. Your bot may send this hint if it is actively processing a request and will ignore input from users until the request is complete. | - - - -This example shows how to let Cortana know that user input is expected. The microphone will be left open. -```cs -// Add an InputHint to let Cortana know to expect user input -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -reply.InputHint = InputHints.ExpectingInput; -``` - - - -## Display cards in Cortana - -In addition to spoken responses, Cortana can also display card attachments. Cortana supports the following rich cards: - -| Card type | Description | -|----|----| -| [HeroCard][heroCard] | A card that typically contains a single large image, one or more buttons, and text. | -| [ThumbnailCard][thumbnailCard] | A card that typically contains a single thumbnail image, one or more buttons, and text. | -| [ReceiptCard][receiptCard] | A card that enables a bot to provide a receipt to the user. It typically contains the list of items to include on the receipt, tax and total information, and other text. | -| [SignInCard][signinCard] | A card that enables a bot to request that a user sign-in. It typically contains text and one or more buttons that the user can click to initiate the sign-in process. | - - -See [Card design best practices][CardDesign] to see what these cards look like inside Cortana. For an example of how to use a rich card in a bot, see [Add rich card attachments to messages](bot-builder-dotnet-add-rich-card-attachments.md). - - - - -## Sample: RollerSkill -The code in the following sections comes from a sample Cortana skill for rolling dice. Download the full code for the bot from the [BotBuilder-Samples repository](https://github.com/Microsoft/BotBuilder-Samples/). - -You invoke the skill by saying its [invocation name][InvocationNameGuidelines] to Cortana. For the roller skill, after you [add the bot to the Cortana channel][CortanaChannel] and register it as a Cortana skill, you can invoke it by telling Cortana "Ask Roller" or "Ask Roller to roll dice". - -### Explore the code - - - -To invoke the appropriate dialogs, the activity handlers defined in `RootDispatchDialog.cs` use regular expressions to match the user's input. For example, the handler in the following example is triggered if the user says something like "I'd like to roll some dice". Synonyms are included in the regular expression so that similar utterances will trigger the dialog. -```cs - [RegexPattern("(roll|role|throw|shoot).*(dice|die|dye|bones)")] - [RegexPattern("new game")] - [ScorableGroup(1)] - public async Task NewGame(IDialogContext context, IActivity activity) - { - context.Call(new CreateGameDialog(), AfterGameCreated); - } -``` - -The `CreateGameDialog` dialog sets up a custom game for the bot to play. It uses a `PromptDialog` to ask the user how many sides they want the dice to have and then how many should be rolled. Note that the `PromptOptions` object that is used to initialize the prompt contains a `speak` property for the spoken version of the prompt. - -```cs - [Serializable] - public class CreateGameDialog : IDialog - { - public async Task StartAsync(IDialogContext context) - { - context.UserData.SetValue(Utils.GameDataKey, new GameData()); - - var descriptions = new List() { "4 Sides", "6 Sides", "8 Sides", "10 Sides", "12 Sides", "20 Sides" }; - var choices = new Dictionary>() - { - { "4", new List { "four", "for", "4 sided", "4 sides" } }, - { "6", new List { "six", "sex", "6 sided", "6 sides" } }, - { "8", new List { "eight", "8 sided", "8 sides" } }, - { "10", new List { "ten", "10 sided", "10 sides" } }, - { "12", new List { "twelve", "12 sided", "12 sides" } }, - { "20", new List { "twenty", "20 sided", "20 sides" } } - }; - - var promptOptions = new PromptOptions( - Resources.ChooseSides, - choices: choices, - descriptions: descriptions, - speak: SSMLHelper.Speak(Utils.RandomPick(Resources.ChooseSidesSSML))); // spoken prompt - - PromptDialog.Choice(context, this.DiceChoiceReceivedAsync, promptOptions); - } - - private async Task DiceChoiceReceivedAsync(IDialogContext context, IAwaitable result) - { - GameData game; - if (context.UserData.TryGetValue(Utils.GameDataKey, out game)) - { - int sides; - if (int.TryParse(await result, out sides)) - { - game.Sides = sides; - context.UserData.SetValue(Utils.GameDataKey, game); - } - - var promptText = string.Format(Resources.ChooseCount, sides); - - var promptOption = new PromptOptions(promptText, choices: null, speak: SSMLHelper.Speak(Utils.RandomPick(Resources.ChooseCountSSML))); - - var prompt = new PromptDialog.PromptInt64(promptOption); - context.Call(prompt, this.DiceNumberReceivedAsync); - } - } - - private async Task DiceNumberReceivedAsync(IDialogContext context, IAwaitable result) - { - GameData game; - if (context.UserData.TryGetValue(Utils.GameDataKey, out game)) - { - game.Count = await result; - context.UserData.SetValue(Utils.GameDataKey, game); - } - - context.Done(game); - } - } -``` - -The `PlayGameDialog` renders the results both by displaying them in a `HeroCard` and building a spoken message to say using the `Speak` method. - -```cs - [Serializable] - public class PlayGameDialog : IDialog - { - private const string RollAgainOptionValue = "roll again"; - - private const string NewGameOptionValue = "new game"; - - private GameData gameData; - - public PlayGameDialog(GameData gameData) - { - this.gameData = gameData; - } - - public async Task StartAsync(IDialogContext context) - { - if (this.gameData == null) - { - if (!context.UserData.TryGetValue(Utils.GameDataKey, out this.gameData)) - { - // User started session with "roll again" so let's just send them to - // the 'CreateGameDialog' - context.Done(null); - } - } - - int total = 0; - var randomGenerator = new Random(); - var rolls = new List(); - - // Generate Rolls - for (int i = 0; i < this.gameData.Count; i++) - { - var roll = randomGenerator.Next(1, this.gameData.Sides); - total += roll; - rolls.Add(roll); - } - - // Format rolls results - var result = string.Join(" . ", rolls.ToArray()); - bool multiLine = rolls.Count > 5; - - var card = new HeroCard() - { - Subtitle = string.Format( - this.gameData.Count > 1 ? Resources.CardSubtitlePlural : Resources.CardSubtitleSingular, - this.gameData.Count, - this.gameData.Sides), - Buttons = new List() - { - new CardAction(ActionTypes.ImBack, "Roll Again", value: RollAgainOptionValue), - new CardAction(ActionTypes.ImBack, "New Game", value: NewGameOptionValue) - } - }; - - if (multiLine) - { - card.Text = result; - } - else - { - card.Title = result; - } - - var message = context.MakeMessage(); - message.Attachments = new List() - { - card.ToAttachment() - }; - - // Determine bots reaction for speech purposes - string reaction = "normal"; - - var min = this.gameData.Count; - var max = this.gameData.Count * this.gameData.Sides; - var score = total / max; - if (score == 1) - { - reaction = "Best"; - } - else if (score == 0) - { - reaction = "Worst"; - } - else if (score <= 0.3) - { - reaction = "Bad"; - } - else if (score >= 0.8) - { - reaction = "Good"; - } - - // Check for special craps rolls - if (this.gameData.Type == "Craps") - { - switch (total) - { - case 2: - case 3: - case 12: - reaction = "CrapsLose"; - break; - case 7: - reaction = "CrapsSeven"; - break; - case 11: - reaction = "CrapsEleven"; - break; - default: - reaction = "CrapsRetry"; - break; - } - } - - // Build up spoken response - var spoken = string.Empty; - if (this.gameData.Turns == 0) - { - spoken += Utils.RandomPick(Resources.ResourceManager.GetString($"Start{this.gameData.Type}GameSSML")); - } - - spoken += Utils.RandomPick(Resources.ResourceManager.GetString($"{reaction}RollReactionSSML")); - - message.Speak = SSMLHelper.Speak(spoken); - - // Increment number of turns and store game to roll again - this.gameData.Turns++; - context.UserData.SetValue(Utils.GameDataKey, this.gameData); - - // Send card and bots reaction to user. - message.InputHint = InputHints.AcceptingInput; - await context.PostAsync(message); - - context.Done(null); - } - } -``` -## Next steps - -If your bot is running locally or deployed in the cloud, you can invoke it from Cortana. See [Test a Cortana skill](../bot-service-debug-cortana-skill.md) for the steps required to try out your Cortana skill. - - -## Additional resources -* [The Cortana Skills Kit][CortanaGetStarted] -* [Add speech to messages](bot-builder-dotnet-text-to-speech.md) -* [SSML Reference][SSMLRef] -* [Voice design best practices for Cortana][VoiceDesign] -* [Card design best practices for Cortana][CardDesign] -* [Cortana Dev Center][CortanaDevCenter] -* [Testing and debugging best practices for Cortana][Cortana-TestBestPractice] -* Bot Framework SDK for .NET Reference - -[CortanaGetStarted]: /cortana/getstarted -[BFPortal]: https://dev.botframework.com/ - -[SSMLRef]: https://aka.ms/cortana-ssml -[CortanaDevCenter]: https://developer.microsoft.com/cortana - -[CortanaSpecificEntities]: https://aka.ms/cortana-channel-data -[CortanaAuth]: https://aka.ms/add-auth-cortana-skill - -[InvocationNameGuidelines]: https://aka.ms/cortana-invocation-guidelines -[VoiceDesign]: https://aka.ms/cortana-design-voice -[CardDesign]: https://aka.ms/cortana-design-card -[Cortana-Debug]: https://aka.ms/cortana-enable-debug -[Cortana-Publish]: https://aka.ms/cortana-publish - - -[CortanaChannel]: https://aka.ms/bot-cortana-channel -[Cortana-TestBestPractice]: https://aka.ms/cortana-test-best-practice - -[heroCard]: /dotnet/api/microsoft.bot.connector.herocard - -[thumbnailCard]: /dotnet/api/microsoft.bot.connector.thumbnailcard - -[receiptCard]: /dotnet/api/microsoft.bot.connector.receiptcard - -[signinCard]: /dotnet/api/microsoft.bot.connector.signincard - - diff --git a/articles/dotnet/bot-builder-dotnet-create-messages.md b/articles/dotnet/bot-builder-dotnet-create-messages.md deleted file mode 100644 index 1984bb816..000000000 --- a/articles/dotnet/bot-builder-dotnet-create-messages.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Create messages with the Bot Framework SDK for .NET - Bot Service -description: Learn about commonly-used message properties within the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Create messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Your bot will send **message** [activities](bot-builder-dotnet-activities.md) to communicate information to users, and likewise, will also receive **message** activities from users. -Some messages may simply consist of plain text, while others may contain richer content such as [text to be spoken](bot-builder-dotnet-text-to-speech.md), [suggested actions](bot-builder-dotnet-add-suggested-actions.md), -[media attachments](bot-builder-dotnet-add-media-attachments.md), [rich cards](bot-builder-dotnet-add-rich-card-attachments.md), and [channel-specific data](bot-builder-dotnet-channeldata.md). - -This article describes some of the commonly-used message properties. - -## Customizing a message - -To have more control over the text formatting of your messages, you can create a custom message using the [Activity](https://docs.botframework.com/csharp/builder/sdkreference/dc/d2f/class_microsoft_1_1_bot_1_1_connector_1_1_activity.html) object and set the properties necessary before sending it to the user. - -This sample shows how to create a custom `message` object and set the `Text`, `TextFormat`, and `Local` properties. - -[!code-csharp[Set message properties](../includes/code/dotnet-create-messages.cs#setBasicProperties)] - -The `TextFormat` property of a message can be used to specify the format of the text. The `TextFormat` property can be set to **plain**, **markdown**, or **xml**. The default value for `TextFormat` is **markdown**. - -## Attachments - -The `Attachments` property of a message activity can be used to send and receive simple media attachments -(image, audio, video, file) and rich cards. -For details, see [Add media attachments to messages](bot-builder-dotnet-add-media-attachments.md) and -[Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md). - -## Entities - -The `Entities` property of a message is an array of open-ended schema.org -objects which allows the exchange of common contextual metadata between the channel and bot. - -### Mention entities - -Many channels support the ability for a bot or user to "mention" someone within the context of a conversation. -To mention a user in a message, populate the message's `Entities` property with a `Mention` object. -The `Mention` object contains these properties: - -| Property | Description | -|----|----| -| Type | type of the entity ("mention") | -| Mentioned | `ChannelAccount` object that indicates which user was mentioned | -| Text | text within the `Activity.Text` property that represents the mention itself (may be empty or null) | - -This code example shows how to add a `Mention` entity to the `Entities` collection. - -[!code-csharp[set Mention](../includes/code/dotnet-create-messages.cs#setMention)] - -> [!TIP] -> When attempting to determine user intent, the bot may want to ignore that portion -> of the message where it is mentioned. Call the `GetMentions` method and evaluate -> the `Mention` objects returned in the response. - -### Place objects - -Location-related information can be conveyed -within a message by populating the message's `Entities` property with either -a `Place` object or a `GeoCoordinates` object. - -The `Place` object contains these properties: - -| Property | Description | -|----|----| -| Type | type of the entity ("Place") | -| Address | description or `PostalAddress` object (future) | -| Geo | GeoCoordinates | -| HasMap | URL to a map or `Map` object (future) | -| Name | name of the place | - -The `GeoCoordinates` object contains these properties: - -| Property | Description | -|----|----| -| Type | type of the entity ("GeoCoordinates") | -| Name | name of the place | -| Longitude | longitude of the location (WGS 84) | -| Latitude | latitude of the location (WGS 84) | -| Elevation | elevation of the location (WGS 84) | - -This code example shows how to add a `Place` entity to the `Entities` collection: - -[!code-csharp[set GeoCoordinates](../includes/code/dotnet-create-messages.cs#setGeoCoord)] - -### Consume entities - -To consume entities, use either the `dynamic` keyword or strongly-typed classes. - -This code example shows how to use the `dynamic` keyword to process an entity within the `Entities` property of a message: - -[!code-csharp[examine entity using dynamic keyword](../includes/code/dotnet-create-messages.cs#examineEntity1)] - -This code example shows how to use a strongly-typed class to process an entity within the `Entities` property of a message: - -[!code-csharp[examine entity using typed class](../includes/code/dotnet-create-messages.cs#examineEntity2)] - -## Channel data - -The `ChannelData` property of a message activity can be used to implement channel-specific functionality. -For details, see [Implement channel-specific functionality](bot-builder-dotnet-channeldata.md). - -## Text to speech - -The `Speak` property of a message activity can be used to specify the text to be spoken by your bot on a speech-enabled channel. The `InputHint` property of a message activity can be used to control the state of the client's microphone and input box (if any). For details, see [Add speech to messages](bot-builder-dotnet-text-to-speech.md). - -## Suggested actions - -The `SuggestedActions` property of a message activity can be used to present buttons that the user can tap to provide input. Unlike buttons that appear within rich cards (which remain visible and accessible to the user even after being tapped), buttons that appear within the suggested actions pane will disappear after the user makes a selection. For details, see [Add suggested actions to messages](bot-builder-dotnet-add-suggested-actions.md). - -## Next steps - -A bot and a user can send messages to each other. When the message is more complex, your bot can send a rich card in a message to the user. Rich cards cover many presentation and interaction scenarios commonly needed in most bots. - -> [!div class="nextstepaction"] -> [Send a rich card in a message](bot-builder-dotnet-add-rich-card-attachments.md) - -## Additional resources - -- [Activities overview](bot-builder-dotnet-activities.md) -- [Send and receive activities](bot-builder-dotnet-connector.md) -- [Add media attachments to messages](bot-builder-dotnet-add-media-attachments.md) -- [Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md) -- [Add speech to messages](bot-builder-dotnet-text-to-speech.md) -- [Add suggested actions to messages](bot-builder-dotnet-add-suggested-actions.md) -- [Implement channel-specific functionality](bot-builder-dotnet-channeldata.md) -- Activity class -- IMessageActivity interface - diff --git a/articles/dotnet/bot-builder-dotnet-dialogs.md b/articles/dotnet/bot-builder-dotnet-dialogs.md deleted file mode 100644 index cd721098d..000000000 --- a/articles/dotnet/bot-builder-dotnet-dialogs.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: Dialogs overview (v3 C#) - Bot Service -description: Learn how to use dialogs within the Bot Framework SDK for .NET to model conversations and manage conversation flow. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Dialogs in the Bot Framework SDK for .NET - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-dialogs.md) -> - [Node.js](../nodejs/bot-builder-nodejs-dialog-overview.md) - -When you create a bot using the Bot Framework SDK for .NET, you can use dialogs to model -a conversation and manage [conversation flow](../bot-service-design-conversation-flow.md). -Each dialog is an abstraction that encapsulates its own state in a C# class that implements `IDialog`. -A dialog can be composed with other dialogs to maximize reuse, and a dialog context maintains the [stack of dialogs](../bot-service-design-conversation-flow.md#dialog-stack) that are active in the conversation at any point in time. - -A conversation that comprises dialogs is portable across computers, which makes it possible for your bot implementation to scale. -When you use dialogs in the Bot Framework SDK for .NET, conversation state (the dialog stack and the state of each dialog in the stack) is automatically stored to your choice of [state data](bot-builder-dotnet-state.md) storage. This enables your bot's service code to be stateless, much like a web application that does not need to store session state in web server memory. - -## Echo bot example - -Consider this echo bot example, which describes how to change the bot that's created in the -[Quickstart](bot-builder-dotnet-quickstart.md) tutorial so that it uses dialogs to -exchange messages with the user. - -> [!TIP] -> To follow along with this example, use the instructions in the -> [Quickstart](bot-builder-dotnet-quickstart.md) tutorial to create a bot, and then -> update its **MessagesController.cs** file as described below. - -### MessagesController.cs - -In the Bot Framework SDK for .NET, the [Builder][builderLibrary] library enables you to implement dialogs. -To access the relevant classes, import the `Dialogs` namespace. - -[!code-csharp[Using statement](../includes/code/dotnet-dialogs.cs#usingStatement)] - -Next, add this `EchoDialog` class to **MessagesController.cs** to represent the conversation. - -[!code-csharp[EchoDialog class](../includes/code/dotnet-dialogs.cs#echobot1)] - -Then, wire the `EchoDialog` class to the `Post` method by calling the `Conversation.SendAsync` method. - -[!code-csharp[Post method](../includes/code/dotnet-dialogs.cs#echobot2)] - -### Implementation details - -The `Post` method is marked `async` because Bot Builder uses the C# facilities for handling -asynchronous communication. -It returns a `Task` object, which represents the task that is responsible for sending replies to the -passed-in message. -If there is an exception, the `Task` that is returned by the method will contain the exception information. - -The `Conversation.SendAsync` method is key to implementing dialogs with the Bot Framework SDK -for .NET. It follows the dependency inversion principle and performs these steps: - -1. Instantiates the required components -2. Deserializes the conversation state (the dialog stack and the state of each dialog in the stack) from `IBotDataStore` -3. Resumes the conversation process where the bot suspended and waits for a message -4. Sends the replies -5. Serializes the updated conversation state and saves it back to `IBotDataStore` - -When the conversation first starts, the dialog does not contain state, -so `Conversation.SendAsync` constructs `EchoDialog` and calls its `StartAsync` method. -The `StartAsync` method calls `IDialogContext.Wait` with the continuation delegate -to specify the method that should be called when a new message is received (`MessageReceivedAsync`). - -The `MessageReceivedAsync` method waits for a message, posts a response, and waits for the next message. -Every time `IDialogContext.Wait` is called, the bot enters a suspended state and can be restarted on any -computer that receives the message. - -A bot that's created by using the code samples above will reply to each message that the user sends by simply -echoing back the user's message prefixed with the text 'You said: '. -Because the bot is created using dialogs, it can evolve to support more complex conversations without having -to explicitly manage state. - -## Echo bot with state example - -This next example builds upon the one above by adding the ability to track dialog state. -When the `EchoDialog` class is updated as shown in the code sample below, -the bot will reply to each message that the user sends by echoing back the user's -message prefixed with a number (`count`) followed by the text 'You said: '. -The bot will continue to increment `count` with each reply, until the user elects to reset the count. - -### MessagesController.cs - -[!code-csharp[EchoDialog class](../includes/code/dotnet-dialogs.cs#echobot3)] - -### Implementation details - -As in the first example, the `MessageReceivedAsync` method is called when a new message is received. -This time though, the `MessageReceivedAsync` method evaluates the user's message before responding. -If the user's message is "reset", the built-in `PromptDialog.Confirm` prompt spawns a sub-dialog that -asks the user to confirm the count reset. -The sub-dialog has its own private state that does not interfere with the parent dialog's state. -When the user responds to the prompt, the result of the sub-dialog is passed to the `AfterResetAsync` method, -which sends a message to the user to indicate whether or not the count was reset and then -calls `IDialogContext.Wait` with a continuation back to `MessageReceivedAsync` on the next message. - -## Dialog context - -The `IDialogContext` interface that is passed into each dialog method -provides access to the services that a dialog requires to save state and communicate with the channel. -The `IDialogContext` interface comprises three interfaces: [Internals.IBotData][iBotData], -[Internals.IBotToUser][iBotToUser], and [Internals.IDialogStack][iDialogStack]. - -### Internals.IBotData - -The `Internals.IBotData` interface provides access to the -per-user, per-conversation, and private conversation state data that's maintained by Connector. -Per-user state data is useful for storing user data that is not related to a specific conversation, -while per-conversation data is useful for storing general data about a conversation, and -private conversation data is useful for storing user data that is related to a specific conversation. - -### Internals.IBotToUser - -`Internals.IBotToUser` provides methods to send a message from bot to user. -Messages may be sent inline with the response to the web API method call or -directly by using the [Connector client](bot-builder-dotnet-connector.md#create-a-connector-client). -Sending and receiving messages through the dialog context ensures that the `Internals.IBotData` state is passed through the Connector. - -### Internals.IDialogStack - -`Internals.IDialogStack` provides methods to manage the [dialog stack](../bot-service-design-conversation-flow.md#dialog-stack). Most of the time, the dialog stack will -automatically be managed for you. However, there may be cases where you want to explictly manage the stack. -For example, you might want to call a child dialog and add it to the -top of the dialog stack, mark the current dialog as complete (thereby removing it from the dialog stack and returning the result to the prior dialog in the stack), -suspend the current dialog until a message from the user arrives, or even reset the dialog stack altogether. - -## Serialization - -The dialog stack and the state of all active dialogs are serialized to the per-user, per-conversation -[IBotDataBag][iBotDataBag]. -The serialized blob is persisted in the messages that the bot sends to and receives from -the [Connector](bot-builder-dotnet-concepts.md#connector). -To be serialized, a `Dialog` class must include the `[Serializable]` attribute. -All `IDialog` implementations in the [Builder][builderLibrary] library are marked as serializable. - -The [Chain methods](#dialog-chains) provide a fluent interface to dialogs that is usable in LINQ query syntax. -The compiled form of LINQ query syntax often uses anonymous methods. -If these anonymous methods do not reference the environment of local variables, then these anonymous methods have no state and are trivially serializable. -However, if the anonymous method captures any local variable in the environment, -the resulting closure object (generated by the compiler) is not marked as serializable. -In this situation, Bot Builder will throw a `ClosureCaptureException` to identify the issue. - -To use reflection to serialize classes that are not marked as serializable, the -Builder library includes a reflection-based serialization surrogate that you can use to register with [Autofac][autofac]. - -[!code-csharp[Serialization](../includes/code/dotnet-dialogs.cs#serialization)] - -## Dialog chains - -While you can explicitly manage the stack of active dialogs by using `IDialogStack.Call` and `IDialogStack.Done`, you can also implicitly manage the stack of -active dialogs by using these fluent [Chain][chain] methods. - - -| Method | Type | Notes | -|-----------------------------|---------|------------------------------------------------------------------------| -| Chain.Select | LINQ | Supports "select" and "let" in LINQ query syntax. | -| Chain.SelectMany | LINQ | Supports successive "from" in LINQ query syntax. | -| Chain.Where | LINQ | Supports "where" in LINQ query syntax. | -| Chain.From | Chains | Instantiates a new instance of a dialog. | -| Chain.Return | Chains | Returns a constant value into the chain. | -| Chain.Do | Chains | Allows for side-effects within the chain. | -| Chain.ContinueWith | Chains | Simple chaining of dialogs. | -| Chain.Unwrap | Chains | Unwrap a dialog nested in a dialog. | -| Chain.DefaultIfException | Chains | Swallows an exception from the previous result and returns default(T). | -| Chain.Loop | Branch | Loops the entire chain of dialogs. | -| Chain.Fold | Branch | Folds results from an enumeration of dialogs into a single result. | -| Chain.Switch | Branch | Supports branching into different dialog chains. | -| Chain.PostToUser | Message | Posts a message to the user. | -| Chain.WaitToBot | Message | Waits for a message to the bot. | -| Chain.PostToChain | Message | Starts a chain with a message from the user. | - -### Examples - -The LINQ query syntax uses the `Chain.Select` method. - -[!code-csharp[Chain.Select](../includes/code/dotnet-dialogs.cs#chain1)] - -Or the `Chain.SelectMany` method. - -[!code-csharp[Chain.SelectMany](../includes/code/dotnet-dialogs.cs#chain2)] - -The `Chain.PostToUser` and `Chain.WaitToBot` methods post messages from the bot to the user and vice versa. - -[!code-csharp[Chain.PostToUser](../includes/code/dotnet-dialogs.cs#chain3)] - -The `Chain.Switch` method branches the conversation dialog flow. - -[!code-csharp[Chain.Switch](../includes/code/dotnet-dialogs.cs#chain4)] - -If `Chain.Switch` returns a nested `IDialog>`, then the inner `IDialog` can be unwrapped with `Chain.Unwrap`. This allows branching conversations to different paths of chained dialogs, possibly of unequal length. This example shows a more complete example of branching dialogs written in the fluent chain style with implicit stack management. - -[!code-csharp[Chain.Switch](../includes/code/dotnet-dialogs.cs#chain5)] - -## Next steps - -Dialogs manage conversation flow between a bot and a user. A dialog defines how to interact with a user. A bot can use many dialogs organized in stacks to guide the conversation with the user. In the next section, see how the dialog stack grows and shrinks as you create and dismiss dialogs in the stack. - -> [!div class="nextstepaction"] -> [Manage conversation flow with dialogs](bot-builder-dotnet-manage-conversation-flow.md) - - -[builderLibrary]: /dotnet/api/microsoft.bot.builder.dialogs - -[iBotData]: /dotnet/api/microsoft.bot.builder.dialogs.internals.ibotdata - -[iBotToUser]: /dotnet/api/microsoft.bot.builder.dialogs.internals.ibottouser - -[iDialogStack]: /dotnet/api/microsoft.bot.builder.dialogs.internals.idialogstack - -[iBotDataBag]: /dotnet/api/microsoft.bot.builder.dialogs.ibotdatabag - -[autofac]: /dotnet/api/microsoft.bot.builder.autofac.base - -[chain]: /dotnet/api/microsoft.bot.builder.dialogs.chain diff --git a/articles/dotnet/bot-builder-dotnet-formflow-advanced.md b/articles/dotnet/bot-builder-dotnet-formflow-advanced.md deleted file mode 100644 index c9c47ee5b..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow-advanced.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: Advanced features of FormFlow - Bot Service -description: Learn how to customize user experience using FormFlow and the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Advanced features of FormFlow - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[Basic features of FormFlow](bot-builder-dotnet-formflow.md) describes a basic FormFlow -implementation that delivers a fairly generic user experience. -To deliver a more customized user experience using FormFlow, you can specify initial form state, -add business logic to manage interdependencies between fields and process user input, -and use attributes to customize prompts, override templates, -designate optional fields, match user input, and validate user input. - -## Specify initial form state and entities - -When you launch a [FormDialog][formDialog], you may optionally pass in an instance of your state. -If you do pass in an instance of your state, then by default, -FormFlow will skip steps for any fields that already contain values; -the user will not be prompted for those fields. -To force the form to prompt the user for all fields (including those fields that already contain values in the -initial state), pass in [FormOptions.PromptFieldsWithValues][promptFieldsWithValues] when you launch -the `FormDialog`. If a field contains an initial value, the prompt will use that value as the default value. - -You can also pass in [LUIS](https://luis.ai/) entities to bind to the state. -If the `EntityRecommendation.Type` is a path to a field in your C# class, -the `EntityRecommendation.Entity` will be passed through the recognizer to bind to your field. -FormFlow will skip steps for any fields that are bound to an entity; -the user will not be prompted for those fields. - -## Add business logic - -To handle interdependencies between form fields or apply specific logic during the process of getting or setting -a field value, you can specify business logic within a validation function. -A validation function lets you manipulate the state and return a [ValidateResult][validateResult] object that can contain: - -- a feedback string that describes the reason that a value is invalid -- a transformed value -- a set of choices for clarifying a value - -This code example shows a validation function for the `Toppings` field. -If input for the field contains the `ToppingOptions.Everything` enumeration value, the function -ensures that the `Toppings` field value contains the full list of toppings. - -[!code-csharp[Validation function](../includes/code/dotnet-formflow-advanced.cs#validationFunction)] - -In addition to the validation function, you can add the [Term](#match-user-input-using-the-terms-attribute) attribute -to match user expressions such as "everything" or "not". - -[!code-csharp[Terms for Toppings](../includes/code/dotnet-formflow-advanced.cs#toppingsTerms)] - -Using the validation function shown above, this snippet shows the -interaction between bot and user when the user requests "everything but Jalapenos." - -```console -Please select one or more toppings (current choice: No Preference) - 1. Everything - 2. Avocado - 3. Banana Peppers - 4. Cucumbers - 5. Green Bell Peppers - 6. Jalapenos - 7. Lettuce - 8. Olives - 9. Pickles - 10. Red Onion - 11. Spinach - 12. Tomatoes -> everything but jalapenos -For sandwich toppings you have selected Avocado, Banana Peppers, Cucumbers, Green Bell Peppers, Lettuce, Olives, Pickles, Red Onion, Spinach, and Tomatoes. -``` - -## FormFlow attributes - -You can add these C# attributes to your class to customize behavior of a FormFlow dialog. - -| Attribute | Purpose | -|----|----| -| [Describe][describeAttribute] | Alter how a field or a value is shown in a template or card | -| [Numeric][numericAttribute] | Restrict the accepted values of a numeric field | -| [Optional][optionalAttribute] | Mark a field as optional | -| [Pattern][patternAttribute] | Define a regular expression to validate a string field | -| [Prompt][promptAttribute] | Define the prompt for a field | -| [Template][templateAttribute] | Define the template to use to generate prompts or values in prompts | -| [Terms][termsAttribute] | Define the input terms that match a field or value | - -## Customize prompts using the Prompt attribute - -Default prompts are automatically generated for each field in your form, -but you can specify a custom prompt for any field by using the `Prompt` attribute. -For example, if the default prompt for the `SandwichOrder.Sandwich` field is "Please select a sandwich", -you can add the `Prompt` attribute to specify a custom prompt for that field. - -[!code-csharp[Prompt attribute](../includes/code/dotnet-formflow-advanced.cs#promptAttribute)] - -This example uses [pattern language](bot-builder-dotnet-formflow-pattern-language.md) -to dynamically populate the prompt with form data at runtime: `{&}` is replaced with the description -of the field and `{||}` is replaced with the list of choices in the enumeration. - -> [!NOTE] -> By default, the description of a field is generated from the field's name. -> To specify a custom description for a field, add the `Describe` attribute. - -This snippet shows the customized prompt that is specified by the example above. - -```console -What kind of sandwich would you like? -1. BLT -2. Black Forest Ham -3. Buffalo Chicken -4. Chicken And Bacon Ranch Melt -5. Cold Cut Combo -6. Meatball Marinara -7. Oven Roasted Chicken -8. Roast Beef -9. Rotisserie Style Chicken -10. Spicy Italian -11. Steak And Cheese -12. Sweet Onion Teriyaki -13. Tuna -14. Turkey Breast -15. Veggie -> -``` - -A `Prompt` attribute may also specify parameters that affect how the form displays the prompt. -For example, the `ChoiceFormat` parameter determines how the form renders the list of choices. - -[!code-csharp[Prompt attribute ChoiceFormat parameter](../includes/code/dotnet-formflow-advanced.cs#promptChoice)] - -In this example, the value of the `ChoiceFormat` parameter indicates that the choices should be -displayed as a bulleted list (instead of a numbered list). - -```console -What kind of sandwich would you like? -- BLT -- Black Forest Ham -- Buffalo Chicken -- Chicken And Bacon Ranch Melt -- Cold Cut Combo -- Meatball Marinara -- Oven Roasted Chicken -- Roast Beef -- Rotisserie Style Chicken -- Spicy Italian -- Steak And Cheese -- Sweet Onion Teriyaki -- Tuna -- Turkey Breast -- Veggie -> -``` - -## Customize prompts using the Template attribute - -While the `Prompt` attribute enables you to customize the prompt for a single field, -the `Template` attribute enables you to replace the default templates that FormFlow uses to automatically -generate prompts. -This code example uses the `Template` attribute to redefine how the form handles -all enumeration fields. The attribute indicates that the user may select only one item, -sets the prompt text by using [pattern language](bot-builder-dotnet-formflow-pattern-language.md), -and specifies that the form should display only one item per line. - -[!code-csharp[Template attribute](../includes/code/dotnet-formflow-advanced.cs#templateAttribute)] - -This snippet shows the resulting prompts for the `Bread` field and `Cheese` field. - -```console -What kind of bread would you like on your sandwich? - 1. Nine Grain Wheat - 2. Nine Grain Honey Oat - 3. Italian - 4. Italian Herbs And Cheese - 5. Flatbread -> - -What kind of cheese would you like on your sandwich? - 1. American - 2. Monterey Cheddar - 3. Pepperjack -> -``` - -If you use the `Template` attribute to replace the default templates that FormFlow uses to -generate prompts, you may want to interject some variation into the prompts and messages -that the form generates. -To do so, you can define multiple text strings using -[pattern language](bot-builder-dotnet-formflow-pattern-language.md), and the form will randomly choose -from the available options each time it needs to display a prompt or message. - -This code example redefines the [TemplateUsage.NotUnderstood][notUnderstood] -template to specify two different variations of -message. When the bot needs to communicate that it does not understand a user's input, it will -determine message contents by randomly selecting one of the two text strings. - -[!code-csharp[Template variations of message](../includes/code/dotnet-formflow-advanced.cs#templateMessages)] - -This snippet shows an example of the resulting the interaction between bot and user. - -```console -What size of sandwich do you want? (1. Six Inch, 2. Foot Long) -> two feet -I do not understand "two feet". -> two feet -Try again, I don't get "two feet" -> -``` - -## Designate a field as optional using the Optional attribute - -To designate a field as optional, use the `Optional` attribute. -This code example specifies that the `Cheese` field is optional. - -[!code-csharp[Optional attribute](../includes/code/dotnet-formflow-advanced.cs#optionalAttribute)] - -If a field is optional and no value has been specified, -the current choice will be displayed as "No Preference". - -```console -What kind of cheese would you like on your sandwich? (current choice: No Preference) - 1. American - 2. Monterey Cheddar - 3. Pepperjack - > -``` - -If a field is optional and the user has specified a value, -"No Preference" will be displayed as the last choice in the list. - -```console -What kind of cheese would you like on your sandwich? (current choice: American) - 1. American - 2. Monterey Cheddar - 3. Pepperjack - 4. No Preference -> -``` - -## Match user input using the Terms attribute - -When a user sends a message to a bot that is built using FormFlow, -the bot attempts to identify the meaning of the user's input by matching the input to a list of terms. -By default, the list of terms is generated by applying these steps to the field or value: - -1. Break on case changes and underscore (_). -2. Generate each n-gram up to a maximum length. -3. Add "s?" to the end of each word (to support plurals). - -For example, the value "AngusBeefAndGarlicPizza" would generate these terms: - -- 'angus?' -- 'beefs?' -- 'garlics?' -- 'pizzas?' -- 'angus? beefs?' -- 'garlics? pizzas?' -- 'angus beef and garlic pizza' - -To override this default behavior and define the list of terms that are used to match -user input to a field or a value in a field, use the `Terms` attribute. -For example, you may use the `Terms` attribute (with a regular expression) to account for the fact that users are -likely to misspell the word "rotisserie." - -[!code-csharp[Terms attribute](../includes/code/dotnet-formflow-advanced.cs#termsAttribute)] - -By using the `Terms` attribute, you increase the likelihood of -being able to match user input with one of the valid choices. -The `Terms.MaxPhrase` parameter in this example causes the `Language.GenerateTerms` to generate additional variations of terms. - -This snippet shows the resulting interaction between bot and user when the user misspells "Rotisserie." - -```console -What kind of sandwich would you like? - 1. BLT - 2. Black Forest Ham - 3. Buffalo Chicken - 4. Chicken And Bacon Ranch Melt - 5. Cold Cut Combo - 6. Meatball Marinara - 7. Oven Roasted Chicken - 8. Roast Beef - 9. Rotisserie Style Chicken - 10. Spicy Italian - 11. Steak And Cheese - 12. Sweet Onion Teriyaki - 13. Tuna - 14. Turkey Breast - 15. Veggie -> rotissary checkin -For sandwich I understood Rotisserie Style Chicken. "checkin" is not an option. -``` - -## Validate user input using the Numeric attribute or Pattern attribute - -To restrict the range of allowed values for a numeric field, use the `Numeric` attribute. -This code example uses the `Numeric` attribute to specify that input for the `Rating` field -must be a number between 1 and 5. - -[!code-csharp[Numeric attribute](../includes/code/dotnet-formflow-advanced.cs#numericAttribute)] - -To specify the required format for the value of a particular field, use the `Pattern` attribute. -This code example uses the `Pattern` attribute to specify the required format for the value of the -`PhoneNumber` field. - -[!code-csharp[Pattern attribute](../includes/code/dotnet-formflow-advanced.cs#patternAttribute)] - -## Summary - -This article has described how to deliver a customized user experience with FormFlow -by specifying initial form state, -adding business logic to manage interdependencies between fields and process user input, -and using attributes to customize prompts, override templates, -designate optional fields, match user input, and validate user input. -For information about additional ways to customize the user experience with FormFlow, -see [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md). - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Additional resources - -- [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -- [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -- [Localize form content](bot-builder-dotnet-formflow-localize.md) -- [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md) -- [Customize user experience with pattern language](bot-builder-dotnet-formflow-pattern-language.md) -- Bot Framework SDK for .NET Reference - -[formDialog]: /dotnet/api/microsoft.bot.builder.formflow.formdialog - -[promptFieldsWithValues]: /dotnet/api/microsoft.bot.builder.formflow.formoptions.promptfieldswithvalues - -[validateResult]: /dotnet/api/microsoft.bot.builder.formflow.validateresult - -[describeAttribute]: /dotnet/api/microsoft.bot.builder.formflow.describeattribute - -[numericAttribute]: /dotnet/api/microsoft.bot.builder.formflow.numericattribute - -[optionalAttribute]: /dotnet/api/microsoft.bot.builder.formflow.optionalattribute - -[patternAttribute]: /dotnet/api/microsoft.bot.builder.formflow.patternattribute - -[promptAttribute]: /dotnet/api/microsoft.bot.builder.formflow.promptattribute - -[templateAttribute]: /dotnet/api/microsoft.bot.builder.formflow.templateattribute - -[termsAttribute]: /dotnet/api/microsoft.bot.builder.formflow.termsattribute - -[notUnderstood]: /dotnet/api/microsoft.bot.builder.formflow.templateusage.notunderstood diff --git a/articles/dotnet/bot-builder-dotnet-formflow-formbuilder.md b/articles/dotnet/bot-builder-dotnet-formflow-formbuilder.md deleted file mode 100644 index 3d83e5065..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow-formbuilder.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Customize a form using FormBuilder - Bot Service -description: Learn how to dynamically change and customize the conversation flow and contents using FormBuilder for the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Customize a form using FormBuilder - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[Basic features of FormFlow](bot-builder-dotnet-formflow.md) describes a basic FormFlow implementation that -delivers a fairly generic user experience, and [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) describes how you can -customize user experience by using business logic and attributes. -This article describes how you can use -[FormBuilder][formBuilder] to customize user experience even further, by -specifying the sequence in which the form executes steps -and dynamically defining field values, confirmations, and messages. - -## Dynamically define field values, confirmations, and messages - -Using FormBuilder, you can dynamically define field values, confirmations, and messages. - -### Dynamically define field values - -A sandwich bot that is designed to add a free drink or cookie to any order that specifies a foot-long sandwich -uses the `Sandwich.Specials` field to store data about free items. -In this case, the value of the `Sandwich.Specials` field must be dynamically set -for each order according to whether or not the order contains a foot-long sandwich. - -The `Specials` field is specified as optional and "None" is designated as text for the choice that indicates no preference. - -[!code-csharp[Field definition](../includes/code/dotnet-formflow-formbuilder.cs#fieldDefinition)] - -This code example shows how to dynamically set the value of the `Specials` field. - -[!code-csharp[Define value](../includes/code/dotnet-formflow-formbuilder.cs#defineValue)] - -In this example, the [Advanced.Field.SetType][setType] method specifies -the field type (`null` represents an enumeration field). -The [Advanced.Field.SetActive][setActive] method specifies that the field -should only be enabled if the length of the sandwich is `Length.FootLong`. -Finally, the [Advanced.Field.SetDefine][setDefine] method specifies an async -delegate that defines the field. -The delegate is passed the current state object and the [Advanced.Field][field] that is being dynamically defined. -The delegate uses the field's fluent methods to dynamically define values. -In this example, the values are strings and the `AddDescription` and `AddTerms` methods specify the descriptions and terms for each value. - -> [!NOTE] -> To dynamically define a field value, you can implement -> [Advanced.IField][iField] yourself, -> or streamline the process by using the [Advanced.FieldReflector][FieldReflector] class as shown in the example above. - -### Dynamically define messages and confirmations - -Using FormBuilder, you can also dynamically define messages and confirmations. -Each message and confirmation runs only when prior steps in the form are inactive or completed. - -This code example shows a dynamically generated confirmation that computes the cost of the sandwich. - -[!code-csharp[Define confirmation](../includes/code/dotnet-formflow-formbuilder.cs#defineConfirmation)] - -## Customize a form using FormBuilder - -This code example uses FormBuilder to define the steps of the form, -[validate selections](bot-builder-dotnet-formflow-advanced.md#add-business-logic), -and [dynamically define a field value and confirmation](#dynamically-define-field-values-confirmations-and-messages). -By default, steps in the form will be executed in the sequence in which they are listed. -However, steps might be skipped for fields that already contain values or if explicit navigation is specified. - -[!code-csharp[FormBuilder form](../includes/code/dotnet-formflow-formbuilder.cs#formBuilderForm)] - -In this example, the form executes these steps: - -- Shows a welcome message. -- Fills in `SandwichOrder.Sandwich`. -- Fills in `SandwichOrder.Length`. -- Fills in `SandwichOrder.Bread`. -- Fills in `SandwichOrder.Cheese`. -- Fills in `SandwichOrder.Toppings` and adds missing values if the user selected `ToppingOptions.Everything`. --. Shows a message that confirms the selected toppings. -- Fills in `SandwichOrder.Sauces`. -- [Dynamically defines](#dynamically-define-field-values) the field value for `SandwichOrder.Specials`. -- [Dynamically defines](#dynamically-define-messages-and-confirmations) the confirmation for cost of the sandwich. -- Fills in `SandwichOrder.DeliveryAddress` and [verifies](bot-builder-dotnet-formflow-advanced.md#add-business-logic) the resulting string. If the address does not start with a number, the form returns a message. -- Fills in `SandwichOrder.DeliveryTime` with a custom prompt. -- Confirms the order. -- Adds any remaining fields that were defined in the class but not explicitly referenced by `Field`. (If the example did not call the `AddRemainingFields` method, the form would not include any fields that were not explicity referenced.) -- Shows a thank you message. -- Defines an `OnCompletionAsync` handler to process the order. - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Additional resources - -- [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -- [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) -- [Localize form content](bot-builder-dotnet-formflow-localize.md) -- [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md) -- [Customize user experience with pattern language](bot-builder-dotnet-formflow-pattern-language.md) -- Bot Framework SDK for .NET Reference - -[formBuilder]: /dotnet/api/microsoft.bot.builder.formflow.formbuilder-1 - -[setType]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1.settype - -[setActive]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1.setactive - -[setDefine]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1.setdefine - -[field]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1 - -[iField]: /dotnet/api/microsoft.bot.builder.formflow.advanced.ifield-1 - -[FieldReflector]: /dotnet/api/microsoft.bot.builder.formflow.advanced.fieldreflector-1 diff --git a/articles/dotnet/bot-builder-dotnet-formflow-json-schema.md b/articles/dotnet/bot-builder-dotnet-formflow-json-schema.md deleted file mode 100644 index c7baa5a10..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow-json-schema.md +++ /dev/null @@ -1,351 +0,0 @@ ---- -title: Define a form using JSON schema and FormFlow - Bot Service -description: Learn how to define a form using JSON schema and FormFlow with the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Define a form using JSON schema - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -If you use a [C# class](bot-builder-dotnet-formflow.md#create-class) to define the form -when you create a bot with FormFlow, -the form derives from the static definition of your type in C#. -As an alternative, you may instead define the form by using -JSON schema. -A form that is defined using JSON schema is purely data-driven; -you can change the form (and therefore, the behavior of the bot) simply by updating the schema. - -The JSON schema describes the fields within your -JObject -and -includes annotations that control prompts, templates, and terms. -To use JSON schema with FormFlow, you must add the `Microsoft.Bot.Builder.FormFlow.Json` NuGet package to your -project and import the `Microsoft.Bot.Builder.FormFlow.Json` namespace. - -## Standard keywords - -FormFlow supports these standard JSON Schema -keywords: - -| Keyword | Description | -|----|----| -| type | Defines the type of data that the field contains. | -| enum | Defines the valid values for the field. | -| minimum | Defines the minimum numeric value allowed for the field (as described in [NumericAttribute][numericAttribute]). | -| maximum | Defines the maximum numeric value allowed for the field (as described in [NumericAttribute][numericAttribute]). | -| required | Defines which fields are required. | -| pattern | Validates string values (as described in [PatternAttribute][patternAttribute]). | - -## Extensions to JSON Schema - -FormFlow extends the standard JSON Schema -to support several additional properties. - -### Additional properties at the root of the schema - -| Property | Value | -|----|----| -| OnCompletion | C# script with arguments `(IDialogContext context, JObject state)` for completing the form. | -| References | References to include in scripts. For example, `[assemblyReference, ...]`. Paths should be absolute or relative to the current directory. By default, the script includes `Microsoft.Bot.Builder.dll`. | -| Imports | Imports to include in scripts. For example, `[import, ...]`. By default, the script includes the `Microsoft.Bot.Builder`, `Microsoft.Bot.Builder.Dialogs`, `Microsoft.Bot.Builder.FormFlow`, `Microsoft.Bot.Builder.FormFlow.Advanced`, `System.Collections.Generic`, and `System.Linq` namespaces. | - -### Additional properties at the root of the schema or as peers of the type property - -| Property | Value | -|----|----| -| Templates | `{ TemplateUsage: { Patterns: [string, ...], }, ...}` | -| Prompt | `{ Patterns:[string, ...] }` | - -To specify templates and prompts in JSON schema, use the same vocabulary as defined by -[TemplateAttribute][templateAttribute] and [PromptAttribute][promptAttribute]. -Property names and values in the schema should match the property names and values in the underlying C# enumeration. -For example, this schema snippet defines a template that overrides the `TemplateUsage.NotUnderstood` template and specifies a `TemplateBaseAttribute.ChoiceStyle`: - -```json -"Templates":{ "NotUnderstood": { "Patterns": ["I don't get it"], "ChoiceStyle":"Auto"}} -``` - -### Additional properties as peers of the type property - -| Property | Contents | Description | -|--------------|-----------------------------|------------------------------------------------------------------------------------------------------------------| -| DateTime | bool | Indicates whether field is a `DateTime` field. | -| Describe | string or object | Description of a field as described in [DescribeAttribute][describeAttribute]. | -| Terms | `[string,...]` | Regular expressions for matching a field value as described in TermsAttribute. | -| MaxPhrase | int | Runs your terms through `Language.GenerateTerms(string, int)` to expand them. | -| Values | `{ string: {Describe:string | object, Terms:[string, ...], MaxPhrase}, ...}` | -| Active | script | C# script with arguments `(JObject state)->bool` to test whether the field, message, or confirmation is active. | -| Validate | script | C# script with arguments `(JObject state, object value)->ValidateResult` for validating a field value. | -| Define | script | C# script with arguments `(JObject state, Field field)` for dynamically defining a field. | -| Next | script | C# script with arguments `(object value, JObject state)` for determining the next step after filling in a field. | -| Before | `[confirm | message, ...]` | -| After | `[confirm | message, ...]` | -| Dependencies | [string, ...] | Fields that this field, message, or confirmation depends on. | - -Use `{Confirm:script|[string, ...], ...templateArgs}` within the value of the **Before** property or -the **After** property to define a confirmation by using either a C# script with argument `(JObject state)` -or a set of patterns that will be randomly selected with optional template arguments. - -Use `{Message:script|[string, ...] ...templateArgs}` within the value of the **Before** property or -the **After** property to define a message by using either a C# script with argument `(JObject state)` -or a set of patterns that will be randomly selected with optional template arguments. - -## Scripts - -Several of the properties that are described above contain a script as the property value. -A script can be any snippet of C# code that you might normally find in the body of a method. -You can add references by using the **References** property and/or the **Imports** property. -Special global variables include: - -| Variable | Description | -|----|----| -| choice | Internal dispatch for the script to execute. | -| state | `JObject` form state bound for all scripts. | -| ifield | `IField` to allow reasoning over the current field for all scripts except Message/Confirm prompt builders. | -| value | Object value to be validated for **Validate**. | -| field | `Field` to allow dynamically updating a field in **Define**. | -| context | `IDialogContext` context to allow posting results in **OnCompletion**. | - -Fields that are defined via JSON schema have the same ability to extend or override the definitions programatically as any other field. They can also be localized in the same way. - -## JSON schema example - -The simplest way to define a form is to define everything, including any C# code, directly in the -JSON schema. -This example shows the JSON schema for the annotated sandwich bot that is described in -[Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md). - -```json -{ - "References": [ "Microsoft.Bot.Sample.AnnotatedSandwichBot.dll" ], - "Imports": [ "Microsoft.Bot.Sample.AnnotatedSandwichBot.Resource" ], - "type": "object", - "required": [ - "Sandwich", - "Length", - "Ingredients", - "DeliveryAddress" - ], - "Templates": { - "NotUnderstood": { - "Patterns": [ "I do not understand \"{0}\".", "Try again, I don't get \"{0}\"." ] - }, - "EnumSelectOne": { - "Patterns": [ "What kind of {&} would you like on your sandwich? {||}" ], - "ChoiceStyle": "Auto" - } - }, - "properties": { - "Sandwich": { - "Prompt": { "Patterns": [ "What kind of {&} would you like? {||}" ] }, - "Before": [ { "Message": [ "Welcome to the sandwich order bot!" ] } ], - "Describe": { "Image": "https://placeholdit.imgix.net/~text?txtsize=16&txt=Sandwich&w=125&h=40&txttrack=0&txtclr=000&txtfont=bold" }, - "type": [ - "string", - "null" - ], - "enum": [ - "BLT", - "BlackForestHam", - "BuffaloChicken", - "ChickenAndBaconRanchMelt", - "ColdCutCombo", - "MeatballMarinara", - "OvenRoastedChicken", - "RoastBeef", - "RotisserieStyleChicken", - "SpicyItalian", - "SteakAndCheese", - "SweetOnionTeriyaki", - "Tuna", - "TurkeyBreast", - "Veggie" - ], - "Values": { - "RotisserieStyleChicken": { - "Terms": [ "rotis\\w* style chicken" ], - "MaxPhrase": 3 - } - } - }, - "Length": { - "Prompt": { - "Patterns": [ "What size of sandwich do you want? {||}" ] - }, - "type": [ - "string", - "null" - ], - "enum": [ - "SixInch", - "FootLong" - ] - }, - "Ingredients": { - "type": "object", - "required": [ "Bread" ], - "properties": { - "Bread": { - "type": [ - "string", - "null" - ], - "Describe": { - "Title": "Sandwich Bot", - "SubTitle": "Bread Picker" - }, - "enum": [ - "NineGrainWheat", - "NineGrainHoneyOat", - "Italian", - "ItalianHerbsAndCheese", - "Flatbread" - ] - }, - "Cheese": { - "type": [ - "string", - "null" - ], - "enum": [ - "American", - "MontereyCheddar", - "Pepperjack" - ] - }, - "Toppings": { - "type": "array", - "items": { - "type": "integer", - "enum": [ - "Everything", - "Avocado", - "BananaPeppers", - "Cucumbers", - "GreenBellPeppers", - "Jalapenos", - "Lettuce", - "Olives", - "Pickles", - "RedOnion", - "Spinach", - "Tomatoes" - ], - "Values": { - "Everything": { "Terms": [ "except", "but", "not", "no", "all", "everything" ] } - } - }, - "Validate": "var values = ((List) value).OfType(); var result = new ValidateResult {IsValid = true, Value = values} ; if (values != null && values.Contains(\"Everything\")) { result.Value = (from topping in new string[] { \"Avocado\", \"BananaPeppers\", \"Cucumbers\", \"GreenBellPeppers\", \"Jalapenos\", \"Lettuce\", \"Olives\", \"Pickles\", \"RedOnion\", \"Spinach\", \"Tomatoes\"} where !values.Contains(topping) select topping).ToList();} return result;", - "After": [ { "Message": [ "For sandwich toppings you have selected {Ingredients.Toppings}." ] } ] - }, - "Sauces": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string", - "enum": [ - "ChipotleSouthwest", - "HoneyMustard", - "LightMayonnaise", - "RegularMayonnaise", - "Mustard", - "Oil", - "Pepper", - "Ranch", - "SweetOnion", - "Vinegar" - ] - } - } - } - }, - "Specials": { - "Templates": { - "NoPreference": { "Patterns": [ "None" ] } - }, - "type": [ - "string", - "null" - ], - "Active": "return (string) state[\"Length\"] == \"FootLong\";", - "Define": "field.SetType(null).AddDescription(\"cookie\", DynamicSandwich.FreeCookie).AddTerms(\"cookie\", Language.GenerateTerms(DynamicSandwich.FreeCookie, 2)).AddDescription(\"drink\", DynamicSandwich.FreeDrink).AddTerms(\"drink\", Language.GenerateTerms(DynamicSandwich.FreeDrink, 2)); return true;", - "After": [ { "Confirm": "var cost = 0.0; switch ((string) state[\"Length\"]) { case \"SixInch\": cost = 5.0; break; case \"FootLong\": cost=6.50; break;} return new PromptAttribute($\"Total for your sandwich is {cost:C2} is that ok?\");" } ] - }, - "DeliveryAddress": { - "type": [ - "string", - "null" - ], - "Validate": "var result = new ValidateResult{ IsValid = true, Value = value}; var address = (value as string).Trim(); if (address.Length > 0 && (address[0] < '0' || address[0] > '9')) {result.Feedback = DynamicSandwich.BadAddress; result.IsValid = false; } return result;" - }, - "PhoneNumber": { - "type": [ "string", "null" ], - "pattern": "(\\(\\d{3}\\))?\\s*\\d{3}(-|\\s*)\\d{4}" - }, - "DeliveryTime": { - "Templates": { - "StatusFormat": { - "Patterns": [ "{&}: {:t}" ], - "FieldCase": "None" - } - }, - "DateTime": true, - "type": [ - "string", - "null" - ], - "After": [ { "Confirm": [ "Do you want to order your {Length} {Sandwich} on {Ingredients.Bread} {&Ingredients.Bread} with {[{Ingredients.Cheese} {Ingredients.Toppings} {Ingredients.Sauces} to be sent to {DeliveryAddress} {?at {DeliveryTime}}?" ] } ] - }, - "Rating": { - "Describe": "your experience today", - "type": [ - "number", - "null" - ], - "minimum": 1, - "maximum": 5, - "After": [ { "Message": [ "Thanks for ordering your sandwich!" ] } ] - } - }, - "OnCompletion": "await context.PostAsync(\"We are currently processing your sandwich. We will message you the status.\");" -} -``` - -## Implement FormFlow with JSON schema - -To implement FormFlow with a JSON schema, use `FormBuilderJson`, which supports the same fluent interface as `FormBuilder`. This code example shows how to implement the JSON schema for the annotated sandwich bot -that is described in [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md). - -[!code-csharp[Use JSON schema](../includes/code/dotnet-formflow-json-schema.cs#useSchema)] - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Additional resources - -- [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -- [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) -- [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -- [Localize form content](bot-builder-dotnet-formflow-localize.md) -- [Customize user experience with pattern language](bot-builder-dotnet-formflow-pattern-language.md) -- Bot Framework SDK for .NET Reference - -[numericAttribute]: /dotnet/api/microsoft.bot.builder.formflow.numericattribute - -[patternAttribute]: /dotnet/api/microsoft.bot.builder.formflow.patternattribute - -[templateAttribute]: /dotnet/api/microsoft.bot.builder.formflow.templateattribute - -[promptAttribute]: /dotnet/api/microsoft.bot.builder.formflow.promptattribute - -[describeAttribute]: /dotnet/api/microsoft.bot.builder.formflow.describeattribute diff --git a/articles/dotnet/bot-builder-dotnet-formflow-localize.md b/articles/dotnet/bot-builder-dotnet-formflow-localize.md deleted file mode 100644 index ce4d15c2a..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow-localize.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: Localize form content - Bot Service -description: Learn how to localize form content with FormFlow and the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/02/2018 -monikerRange: 'azure-bot-service-3.0' ---- - -# Localize form content - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -A form's localization language is determined by the current thread's [CurrentUICulture](https://msdn.microsoft.com/library/system.threading.thread.currentuiculture(v=vs.110).aspx) and [CurrentCulture](https://msdn.microsoft.com/library/system.threading.thread.currentculture(v=vs.110).aspx). -By default, the culture derives from the **Locale** field of the current message, but you can -override that default behavior. -Depending on how your bot is constructed, localized information may come from up to three different sources: - -- the built-in localization for **PromptDialog** and **FormFlow** -- a resource file that you generate for the static strings in your form -- a resource file that you create with strings for dynamically-computed fields, messages or confirmations - -## Generate a resource file for the static strings in your form - -Static strings in a form include the strings that the form generates from the information in your C# class -and the strings that you specify as prompts, templates, messages or confirmations. -Strings that are generated from built-in templates are not considered static strings, since those strings are already localized. -Since many of the strings in a form are automatically generated, it is not feasible to use normal C# resource strings directly. -Instead, you can generate a resource file for the static strings in your form either by calling -`IFormBuilder.SaveResources` or by using the **RView** tool that is included with the BotBuilder SDK for .NET. - -### Use IFormBuilder.SaveResources - -You can generate a resource file by -calling [IFormBuilder.SaveResources][saveResources] on your form to save the strings to a .resx file. - -### Use RView - -Alternatively, you can generate a resource file that is based upon your .dll or .exe by using -the RView -tool that is included in the BotBuilder SDK for .NET. -To generate the .resx file, execute **rview** and specify the assembly that contains your static form-building method and the path to that method. -This snippet shows how to generate the `Microsoft.Bot.Sample.AnnotatedSandwichBot.SandwichOrder.resx` resource file using **RView**. - -```csharp -rview -g Microsoft.Bot.Sample.AnnotatedSandwichBot.dll Microsoft.Bot.Sample.AnnotatedSandwichBot.SandwichOrder.BuildForm -``` - -This excerpt shows part of the .resx file that is generated by executing this **rview** command. - -```xml - -Specials - - -Delivery Address - - -Delivery Time - - -Phone Number - - -your experience today - - -Welcome to the sandwich order bot! - - -sandwichs? - -``` - -## Configure your project - -After you have generated a resource file, add it to your project and then set the neutral language by -completing these steps: - -1. Right-click on your project and select **Application**. -2. Click **Assembly Information**. -3. Select the **Neutral Language** value that corresponds to the language in which you developed your bot. - -When your form is created, the [IFormBuilder.Build][build] method will automatically look for resources that contain your form type name and use them to localize the static strings in your form. - -> [!NOTE] -> Dynamically-computed fields that are defined using [Advanced.Field.SetDefine][setDefine] -> (as described in [Using Dynamic Fields](bot-builder-dotnet-formflow-formbuilder.md#dynamically-define-field-values-confirmations-and-messages)) -> cannot be localized in the same manner as static fields, -> since strings for dynamically-computed fields are constructed at the time the form is populated. -> However, you can localize dynamically-computed fields by using normal C# localization mechanisms. - -### Localize resource files - -After you have added resource files to your project, you can localize them by using the -Multilingual App Toolkit (MAT). -Install **MAT**, then enable it for your project by completing these steps: - -1. Select your project in the Visual Studio Solution Explorer. -2. Click **Tools**, **Multilingual App Toolkit**, and **Enable**. -3. Right-click the project and select **Multilingual App Toolkit**, **Add Translations** to select the translations. This will create industry-standard XLF files that you can automatically or manually translate. - -> [!NOTE] -> Although this article describes how to use the Multilingual App Toolkit to localize content, -> you may implement localization via a variety of other means. - -## See it in action - -This code example builds upon the one in [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) to implement localization as described above. -In this example, the `DynamicSandwich` class (not shown here) contains localization information for -dynamically-computed fields, messages and confirmations. - -[!code-csharp[Build localized form](../includes/code/dotnet-formflow-localize.cs#buildLocalizedForm)] - -This snippet shows the resulting interaction between bot and user when `CurrentUICulture` is **French**. - -```console -Bienvenue sur le bot d'ordre "sandwich" ! -Quel genre de "sandwich" vous souhaitez sur votre "sandwich"? - 1. BLT - 2. Jambon Forêt Noire - 3. Poulet Buffalo - 4. Faire fondre le poulet et Bacon Ranch - 5. Combo de coupe à froid - 6. Boulette de viande Marinara - 7. Poulet rôti au four - 8. Rôti de boeuf - 9. Rotisserie poulet - 10. Italienne piquante - 11. Bifteck et fromage - 12. Oignon doux Teriyaki - 13. Thon - 14. Poitrine de dinde - 15. Veggie -> 2 - -Quel genre de longueur vous souhaitez sur votre "sandwich"? - 1. Six pouces - 2. Pied Long -> ? -* Vous renseignez le champ longueur.Réponses possibles: -* Vous pouvez saisir un numéro 1-2 ou des mots de la description. (Six pouces, ou Pied Long) -* Retourner à la question précédente. -* Assistance: Montrez les réponses possibles. -* Abandonner: Abandonner sans finir -* Recommencer remplir le formulaire. (Vos réponses précédentes sont enregistrées.) -* Statut: Montrer le progrès en remplissant le formulaire jusqu'à présent. -* Vous pouvez passer à un autre champ en entrant son nom. ("Sandwich", Longueur, Pain, Fromage, Nappages, Sauces, Adresse de remise, Délai de livraison, ou votre expérience aujourd'hui). -Quel genre de longueur vous souhaitez sur votre "sandwich"? - 1. Six pouces - 2. Pied Long -> 1 - -Quel genre de pain vous souhaitez sur votre "sandwich"? - 1. Neuf grains de blé - 2. Neuf grains miel avoine - 3. Italien - 4. Fromage et herbes italiennes - 5. Pain plat -> neuf -Par pain "neuf" vouliez-vous dire (1. Neuf grains miel avoine, ou 2. Neuf grains de blé) -``` - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Additional resources - -- [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -- [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) -- [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -- [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md) -- [Customize user experience with pattern language](bot-builder-dotnet-formflow-pattern-language.md) -- Bot Framework SDK for .NET Reference - -[build]: /dotnet/api/microsoft.bot.builder.formflow.formbuilder-1.build - -[setDefine]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1.setdefine - -[saveResources]: /dotnet/api/microsoft.bot.builder.formflow.iform-1.saveresources diff --git a/articles/dotnet/bot-builder-dotnet-formflow-pattern-language.md b/articles/dotnet/bot-builder-dotnet-formflow-pattern-language.md deleted file mode 100644 index ff3d90b96..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow-pattern-language.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: Customize user experience with pattern language - Bot Service -description: Learn how to customize FormFlow prompts and override FormFlow templates by using pattern language with the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Customize user experience with pattern language - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -When you customize a prompt or override a default template, you can -use pattern language to specify the contents and/or format of the prompt. - -## Prompts and templates - -A [prompt][promptAttribute] defines the message that is sent to the user to request a piece of information or ask for confirmation. You can customize a prompt by using the [Prompt attribute](bot-builder-dotnet-formflow-advanced.md#customize-prompts-using-the-prompt-attribute) or implicitly through [IFormBuilder.Field][field]. - -Forms use templates to automatically construct prompts and other things such as help. -You can override the default template of a class or field by using the [Template attribute](bot-builder-dotnet-formflow-advanced.md#customize-prompts-using-the-template-attribute). - -> [!TIP] -> The [FormConfiguration.Templates][formConfiguration] -> class defines a set of built-in templates that provide good examples of how to use pattern language. - -## Elements of pattern language - -Pattern language uses curly braces (`{}`) to identify elements that will be replaced at runtime with actual -values. This table lists the elements of pattern language. - -| Element | Description | -|----|----| -| `{}` | Shows the value of the current field (the field that the attribute applies to). | -| `{&}` | Shows the description of the current field (unless otherwise specified, this is the name of the field). | -| `{}` | Shows the value of the named field. | -| `{&}` | Shows the description of the named field. | -| {||} | Shows the current choice(s), which could be the current value of a field, "no preference" or the values of an enumeration. | -| `{[{} ...]}` | Shows a list of values from the named fields using [Separator][separator] and [LastSeparator][lastSeparator] to separate the individual values in the list. | -| `{*}` | Shows one line for each active field; each line contains the field description and current value. | -| `{*filled}` | Shows one line for each active field that contains an actual value; each line contains the field description and current value. | -| `{}` | A regular C# format specifier that applies to the nth argument of a template. For the list of available arguments, see [TemplateUsage][templateUsage]. | -| `{?...}` | Conditional substitution. If all referred to pattern elements have values, the values are substituted and the whole expression is used. | - -For the elements listed above: - -- The `` placeholder is the name of a field in your form class. For example, if your class contains a field with the name `Size`, you could specify `{Size}` as the pattern element. - -- An ellipses (`"..."`) within a pattern element indicates that the element may contain multiple values. - -- The `` placeholder is a C# format specifier. For example, if the class contains a field with the name `Rating` and of type `double`, you could specify `{Rating:F2}` as the pattern element to show two digits of precision. - -- The `` placeholder references the nth argument of a template. - -### Pattern language within a Prompt attribute - -This example uses the `{&}` element to show the description of the `Sandwich` field and the -`{||}` element to show the list of choices for that field. - -[!code-csharp[Patterns example](../includes/code/dotnet-formflow-pattern-language.cs#patterns1)] - -This is the resulting prompt: - -```console -What kind of sandwich would you like? -1. BLT -2. Black Forest Ham -3. Buffalo Chicken -4. Chicken And Bacon Ranch Melt -5. Cold Cut Combo -6. Meatball Marinara -7. Oven Roasted Chicken -8. Roast Beef -9. Rotisserie Style Chicken -10. Spicy Italian -11. Steak And Cheese -12. Sweet Onion Teriyaki -13. Tuna -14. Turkey Breast -15. Veggie -> -``` - -## Formatting parameters - -Prompts and templates support these formatting parameters. - -| Usage | Description | -|----|----| -| `AllowDefault` | Applies to {||} pattern elements. Determines whether the form should show the current value of the field as a possible choice. If `true`, the current value is shown as a possible value. The default is `true`. | -| `ChoiceCase` | Applies to {||} pattern elements. Determines whether the text of each choice is normalized (e.g., whether the first letter of each word is capitalized). The default is `CaseNormalization.None`. For possible values, see [CaseNormalization][caseNormalization]. | -| `ChoiceFormat` | Applies to {||} pattern elements. Determines whether to show a list of choices as a numbered list or a bulleted list. For a numbered list, set `ChoiceFormat` to `{0}` (default). For a bulleted list list, set `ChoiceFormat` to `{1}`. | -| `ChoiceLastSeparator` | Applies to {||} pattern elements. Determines whether an inline list of choices includes a separator before the last choice. | -| `ChoiceParens` | Applies to {||} pattern elements. Determines whether an inline list of choices is shown within parentheses. If `true`, the list of choices is shown within parentheses. The default is `true`. | -| `ChoiceSeparator` | Applies to {||} pattern elements. Determines whether an inline list of choices includes a separator before every choice except the last choice. | -| `ChoiceStyle` | Applies to {||} pattern elements. Determines whether the list of choices is shown inline or per line. The default is `ChoiceStyleOptions.Auto` which determines at runtime whether to show the choice inline or in a list. For possible values, see [ChoiceStyleOptions][choiceStyleOptions]. | -| `Feedback` | Applies to prompts only. Determines whether the form echoes the user's choice to indicate that the form understood the selection. The default is `FeedbackOptions.Auto` which echoes the user's input only if part of it is not understood. For possible values, see [FeedbackOptions][feedbackOptions]. | -| `FieldCase` | Determines whether the text of the field's description is normalized (e.g., whether the first letter of each word is capitalized). The default is `CaseNormalization.Lower` which converts the description to lowercase. For possible values, see [CaseNormalization][caseNormalization]. | -| `LastSeparator` | Applies to `{[]}` pattern elements. Determines whether an array of items includes a separator before the last item. | -| `Separator` | Applies to `{[]}` pattern elements. Determines whether an array of items includes a separator before every item in the array except the last item. | -| `ValueCase` | Determines whether the text of the field's value is normalized (e.g., whether the first letter of each word is capitalized)_. The default is `CaseNormalization.InitialUpper` which converts the first letter of each word to uppercase. For possible values, see [CaseNormalization][caseNormalization]. | - -### Prompt attribute with formatting parameter - -This example uses the `ChoiceFormat` parameter to specify that the list of choices should be displayed -as a bulleted list. - -[!code-csharp[Patterns example](../includes/code/dotnet-formflow-pattern-language.cs#patterns2)] - -This is the resulting prompt: - -```console -What kind of sandwich would you like? -* BLT -* Black Forest Ham -* Buffalo Chicken -* Chicken And Bacon Ranch Melt -* Cold Cut Combo -* Meatball Marinara -* Oven Roasted Chicken -* Roast Beef -* Rotisserie Style Chicken -* Spicy Italian -* Steak And Cheese -* Sweet Onion Teriyaki -* Tuna -* Turkey Breast -* Veggie -> -``` - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Additional resources - -- [Basic features of FormFlow](bot-builder-dotnet-formflow.md) -- [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) -- [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -- [Localize form content](bot-builder-dotnet-formflow-localize.md) -- [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md) -- Bot Framework SDK for .NET Reference - -[promptAttribute]: /dotnet/api/microsoft.bot.builder.formflow.promptattribute - -[field]: /dotnet/api/microsoft.bot.builder.formflow.iformbuilder-1.field - -[formConfiguration]: /dotnet/api/microsoft.bot.builder.formflow.formconfiguration - -[separator]: /dotnet/api/microsoft.bot.builder.formflow.advanced.templatebaseattribute.separator - -[lastSeparator]: /dotnet/api/microsoft.bot.builder.formflow.advanced.templatebaseattribute.lastseparator - -[templateUsage]: /dotnet/api/microsoft.bot.builder.formflow.templateusage - -[caseNormalization]: /dotnet/api/microsoft.bot.builder.formflow.casenormalization - -[choiceStyleOptions]: /dotnet/api/microsoft.bot.builder.formflow.choicestyleoptions - -[feedbackOptions]: /dotnet/api/microsoft.bot.builder.formflow.feedbackoptions diff --git a/articles/dotnet/bot-builder-dotnet-formflow.md b/articles/dotnet/bot-builder-dotnet-formflow.md deleted file mode 100644 index 8a0f537cc..000000000 --- a/articles/dotnet/bot-builder-dotnet-formflow.md +++ /dev/null @@ -1,378 +0,0 @@ ---- -title: Basic features of FormFlow - Bot Service -description: Learn how to guide conversation flows using FormFlow within the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Basic features of FormFlow - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[Dialogs](bot-builder-dotnet-dialogs.md) are very powerful and flexible, but handling a guided conversation such as ordering a sandwich can require a lot of effort. At each point in the conversation, there are many possibilities of what will happen next. For example, you may need to clarify an ambiguity, provide help, go back, or show progress. -By using **FormFlow** within the Bot Framework SDK for .NET, you can greatly simplify the process of managing -a guided conversation like this. - -FormFlow automatically generates the dialogs that are necessary to manage a guided conversation, -based upon guidelines that you specify. -Although using FormFlow sacrifices some of the flexibility that you might otherwise get by creating and managing -dialogs on your own, designing a guided conversation using FormFlow can significantly reduce the time it takes -to develop your bot. -Additionally, you may construct your bot using a combination of FormFlow-generated dialogs and other types of -dialogs. For example, a FormFlow dialog may guide the user through the process of completing a form, while a -[LuisDialog][LuisDialog] may evaluate user input to determine intent. - -This article describes how to create a bot that uses the basic features of FormFlow to -collect information from a user. - -## Forms and fields - -To create a bot using FormFlow, you must specify the information that the bot needs to collect from the user. -For example, if the bot's objective is to obtain a user's sandwich order, then you must define a form -that contains fields for the data that the bot needs to fulfill the order. -You can define the form by creating a C# class that contains one or more public properties to -represent the data that the bot will collect from the user. -Each property must be one of these data types: - -- Integral (sbyte, byte, short, ushort, int, uint, long, ulong) -- Floating point (float, double) -- String -- DateTime -- Enumeration -- List of enumerations - -Any of the data types may be nullable, which you can use to model that the field does not have a value. -If a form field is based on an enumeration property that is not nullable, the value **0** in the enumeration represents **null** (i.e., indicates that the field does not have a value), -and you should start your enumeration values at **1**. -FormFlow ignores all other property types and methods. - -For complex objects, you must create a form for the top-level C# class and another form for the complex object. -You can compose the forms together by using typical [dialog](bot-builder-dotnet-dialogs.md) semantics. -It is also possible to define a form directly by implementing [Advanced.IField][iField] -or using [Advanced.Field][field] and populating the dictionaries within it. - -> [!NOTE] -> You can define a form by using either a C# class or JSON schema. -> This article describes how to define a form using a C# class. -> For more information about using JSON schema, see [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md). - -## Simple sandwich bot - -Consider this example of a simple sandwich bot that is designed to obtain a user's sandwich order. - -### Create the form - -The `SandwichOrder` class defines the form and the enumerations define the options for building a sandwich. -The class also includes the static `BuildForm` method that uses [FormBuilder][formBuilder] -to create the form and define a simple welcome message. - -To use FormFlow, you must first import the `Microsoft.Bot.Builder.FormFlow` namespace. - -[!code-csharp[Define form](../includes/code/dotnet-formflow.cs#defineForm)] - -### Connect the form to the framework - -To connect the form to the framework, you must add it to the controller. -In this example, the `Conversation.SendAsync` method calls the static `MakeRootDialog` method, -which in turn, calls the `FormDialog.FromForm` method to create the `SandwichOrder` form. - -[!code-csharp[Connect form to framework](../includes/code/dotnet-formflow.cs#connectToFramework)] - -### See it in action - -By simply defining the form with a C# class and connecting it to the framework, you have enabled FormFlow to -automatically manage the conversation between bot and user. -The example interactions shown below demonstrate the capabilities of a bot that is created by using the -basic features of FormFlow. -In each interaction, a **>** symbol indicates the point at which the user enters a response. - -#### Display the first prompt - -This form populates the `SandwichOrder.Sandwich` property. -The form automatically generates the prompt, "Please select a sandwich", -where the word "sandwich" in the prompt derives from the property name `Sandwich`. -The `SandwichOptions` enumeration defines the choices that are presented to the user, -with each enumeration value being automatically broken into words based upon changes in case and underscores. - -```console -Please select a sandwich -1. BLT -2. Black Forest Ham -3. Buffalo Chicken -4. Chicken And Bacon Ranch Melt -5. Cold Cut Combo -6. Meatball Marinara -7. Oven Roasted Chicken -8. Roast Beef -9. Rotisserie Style Chicken -10. Spicy Italian -11. Steak And Cheese -12. Sweet Onion Teriyaki -13. Tuna -14. Turkey Breast -15. Veggie -> -``` - -#### Provide guidance - -The user can enter "help" at any point in the conversation to get guidance with filling out the form. -For example, if the user enters "help" at the sandwich prompt, the bot will respond with this guidance. - -```console -> help -* You are filling in the sandwich field. Possible responses: -* You can enter a number 1-15 or words from the descriptions. (BLT, Black Forest Ham, Buffalo Chicken, Chicken And Bacon Ranch Melt, Cold Cut Combo, Meatball Marinara, Oven Roasted Chicken, Roast Beef, Rotisserie Style Chicken, Spicy Italian, Steak And Cheese, Sweet Onion Teriyaki, Tuna, Turkey Breast, and Veggie) -* Back: Go back to the previous question. -* Help: Show the kinds of responses you can enter. -* Quit: Quit the form without completing it. -* Reset: Start over filling in the form. (With defaults from your previous entries.) -* Status: Show your progress in filling in the form so far. -* You can switch to another field by entering its name. (Sandwich, Length, Bread, Cheese, Toppings, and Sauce). -``` - -#### Advance to the next prompt - -If the user enters "2" in response to the initial sandwich prompt, -the bot then displays a prompt for the next property that is defined by the form: `SandwichOrder.Length`. - -```console -Please select a sandwich - 1. BLT - 2. Black Forest Ham - 3. Buffalo Chicken - 4. Chicken And Bacon Ranch Melt - 5. Cold Cut Combo - 6. Meatball Marinara - 7. Oven Roasted Chicken - 8. Roast Beef - 9. Rotisserie Style Chicken - 10. Spicy Italian - 11. Steak And Cheese - 12. Sweet Onion Teriyaki - 13. Tuna - 14. Turkey Breast - 15. Veggie -> 2 -Please select a length (1. Six Inch, 2. Foot Long) -> -``` - -#### Return to the previous prompt - -If the user enters "back" at this point in the conversation, the bot will return the previous prompt. -The prompt shows the user's current choice ("Black Forest Ham"); the user may change that selection by -entering a different number or confirm that selection by entering "c". - -```console -> back -Please select a sandwich(current choice: Black Forest Ham) - 1. BLT - 2. Black Forest Ham - 3. Buffalo Chicken - 4. Chicken And Bacon Ranch Melt - 5. Cold Cut Combo - 6. Meatball Marinara - 7. Oven Roasted Chicken - 8. Roast Beef - 9. Rotisserie Style Chicken - 10. Spicy Italian - 11. Steak And Cheese - 12. Sweet Onion Teriyaki - 13. Tuna - 14. Turkey Breast - 15. Veggie -> c -Please select a length (1. Six Inch, 2. Foot Long) -> -``` - -#### Clarify user input - -If the user responds with text (instead of a number) to indicate a choice, -the bot will automatically ask for clarification if user input matches more than one choice. - -```console -Please select a bread - 1. Nine Grain Wheat - 2. Nine Grain Honey Oat - 3. Italian - 4. Italian Herbs And Cheese - 5. Flatbread -> nine grain -By "nine grain" bread did you mean (1. Nine Grain Honey Oat, 2. Nine Grain Wheat) -> 1 -``` - -If user input does not directly match any of the valid choices, the bot will -automatically prompt the user for clarification. - -```console -Please select a cheese (1. American, 2. Monterey Cheddar, 3. Pepperjack) -> amercan -"amercan" is not a cheese option. -> american smoked -For cheese I understood American. "smoked" is not an option. -``` - -If user input specifies multiple choices for a property and the bot does not understand any of the -specified choices, it will automatically prompt the user for clarification. - -```console -Please select one or more toppings - 1. Banana Peppers - 2. Cucumbers - 3. Green Bell Peppers - 4. Jalapenos - 5. Lettuce - 6. Olives - 7. Pickles - 8. Red Onion - 9. Spinach - 10. Tomatoes -> peppers, lettuce and tomato -By "peppers" toppings did you mean (1. Green Bell Peppers, 2. Banana Peppers) -> 1 -``` - -#### Show current status - -If the user enters "status" at any point in the order, the bot's response will indicate -which values have already been specified and which values remain to be specified. - -```console -Please select one or more sauce - 1. Honey Mustard - 2. Light Mayonnaise - 3. Regular Mayonnaise - 4. Mustard - 5. Oil - 6. Pepper - 7. Ranch - 8. Sweet Onion - 9. Vinegar -> status -* Sandwich: Black Forest Ham -* Length: Six Inch -* Bread: Nine Grain Honey Oat -* Cheese: American -* Toppings: Lettuce, Tomatoes, and Green Bell Peppers -* Sauce: Unspecified -``` - -#### Confirm selections - -When the user completes the form, the bot will ask the user to confirm their selections. - -```console -Please select one or more sauce - 1. Honey Mustard - 2. Light Mayonnaise - 3. Regular Mayonnaise - 4. Mustard - 5. Oil - 6. Pepper - 7. Ranch - 8. Sweet Onion - 9. Vinegar -> 1 -Is this your selection? -* Sandwich: Black Forest Ham -* Length: Six Inch -* Bread: Nine Grain Honey Oat -* Cheese: American -* Toppings: Lettuce, Tomatoes, and Green Bell Peppers -* Sauce: Honey Mustard -> -``` - -If the user responds by entering "no", the bot allows the user to update any of the prior selections. -If the user responds by entering "yes", the form has been completed and control is returned to the calling dialog. - -```console -Is this your selection? -* Sandwich: Black Forest Ham -* Length: Six Inch -* Bread: Nine Grain Honey Oat -* Cheese: American -* Toppings: Lettuce, Tomatoes, and Green Bell Peppers -* Sauce: Honey Mustard -> no -What do you want to change? - 1. Sandwich(Black Forest Ham) - 2. Length(Six Inch) - 3. Bread(Nine Grain Honey Oat) - 4. Cheese(American) - 5. Toppings(Lettuce, Tomatoes, and Green Bell Peppers) - 6. Sauce(Honey Mustard) -> 2 -Please select a length (current choice: Six Inch) (1. Six Inch, 2. Foot Long) -> 2 -Is this your selection? -* Sandwich: Black Forest Ham -* Length: Foot Long -* Bread: Nine Grain Honey Oat -* Cheese: American -* Toppings: Lettuce, Tomatoes, and Green Bell Peppers -* Sauce: Honey Mustard -> y -``` - -## Handling quit and exceptions - -If the user enters "quit" in the form or an exception occurs at some point in the conversation, -your bot will need to know the step in which the event occurred, the state of the form when the event occurred, -and which steps of the form were successfully completed prior to the event. -The form returns this information via the `FormCanceledException` class. - -This code example shows how to catch the exception and display a message according to the event that occurred. - -[!code-csharp[Handle exception or quit](../includes/code/dotnet-formflow.cs#handleExceptionOrQuit)] - -## Summary - -This article has described how to use the basic features of FormFlow to create a bot that can: - -- Automatically generate and manage the conversation -- Provide clear guidance and help -- Understand both numbers and textual entries -- Provide feedback to the user regarding what is understood and what is not -- Ask clarifying questions when necessary -- Allow the user to navigate between steps - -Although basic FormFlow functionality is sufficient in some cases, -you should consider the potential benefits of incorporating some of the more advanced -features of FormFlow into your bot. -For more information, see [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) and [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md). - -## Sample code - -[!INCLUDE [Sample code](../includes/snippet-dotnet-formflow-samples.md)] - -## Next steps - -FormFlow simplifies dialog development. The advanced features of FormFlow let you customize how a FormFlow object behaves. - -> [!div class="nextstepaction"] -> [Advanced features of FormFlow](bot-builder-dotnet-formflow-advanced.md) - -## Additional resources - -- [Customize a form using FormBuilder](bot-builder-dotnet-formflow-formbuilder.md) -- [Localize form content](bot-builder-dotnet-formflow-localize.md) -- [Define a form using JSON schema](bot-builder-dotnet-formflow-json-schema.md) -- [Customize user experience with pattern language](bot-builder-dotnet-formflow-pattern-language.md) -- Bot Framework SDK for .NET Reference - -[LuisDialog]: /dotnet/api/microsoft.bot.builder.dialogs.luisdialog-1 - -[iField]: /dotnet/api/microsoft.bot.builder.formflow.advanced.ifield-1 - -[field]: /dotnet/api/microsoft.bot.builder.formflow.advanced.field-1 - -[formBuilder]: /dotnet/api/microsoft.bot.builder.formflow.formbuilder-1 diff --git a/articles/dotnet/bot-builder-dotnet-global-handlers.md b/articles/dotnet/bot-builder-dotnet-global-handlers.md deleted file mode 100644 index 9a70ac067..000000000 --- a/articles/dotnet/bot-builder-dotnet-global-handlers.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Implement global message handlers - Bot Service -description: Learn how to enable your bot to listen for and handle user input containing certain keywords using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Implement global message handlers - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[!INCLUDE [Introduction to global message handlers](../includes/snippet-global-handlers-intro.md)] - -## Listen for keywords in user input - -The following walk through shows how to implement global message handlers by using the Bot Framework SDK for .NET. - -First, `Global.asax.cs` registers `GlobalMessageHandlersBotModule`, which is implemented as shown here. -In this example, the module registers two scorables: one for managing a request to change settings (`SettingsScorable`) -and another for managing a request to cancel (`CancelScoreable`). - -```cs -public class GlobalMessageHandlersBotModule : Module -{ - protected override void Load(ContainerBuilder builder) - { - base.Load(builder); - - builder - .Register(c => new SettingsScorable(c.Resolve())) - .As>() - .InstancePerLifetimeScope(); - - builder - .Register(c => new CancelScorable(c.Resolve())) - .As>() - .InstancePerLifetimeScope(); - } -} -``` - -The `CancelScorable` contains a `PrepareAsync` method that defines the trigger: -if the message text is "cancel", this scorable will be triggered. - -```cs -protected override async Task PrepareAsync(IActivity activity, CancellationToken token) -{ - var message = activity as IMessageActivity; - if (message != null && !string.IsNullOrWhiteSpace(message.Text)) - { - if (message.Text.Equals("cancel", StringComparison.InvariantCultureIgnoreCase)) - { - return message.Text; - } - } - return null; -} -``` - -When a "cancel" request is received, the `PostAsync` method within `CancelScoreable` -resets the dialog stack. - -```cs -protected override async Task PostAsync(IActivity item, string state, CancellationToken token) -{ - this.task.Reset(); -} -``` - -When a "change settings" request is received, the `PostAsync` method within `SettingsScorable` -invokes the `SettingsDialog` (passing the request to that dialog), thereby adding the `SettingsDialog` -to the top of the dialog stack and putting it in control of the conversation. - -```cs -protected override async Task PostAsync(IActivity item, string state, CancellationToken token) -{ - var message = item as IMessageActivity; - if (message != null) - { - var settingsDialog = new SettingsDialog(); - var interruption = settingsDialog.Void(); - this.task.Call(interruption, null); - await this.task.PollAsync(token); - } -} -``` - -## Sample code - -For a complete sample that shows how to implement global message handlers using the Bot Framework SDK for .NET, see the Global Message Handlers sample in GitHub. - -## Additional resources - -- [Design and control conversation flow](../bot-service-design-conversation-flow.md) -- Bot Framework SDK for .NET Reference -- Global Message Handlers sample (GitHub) diff --git a/articles/dotnet/bot-builder-dotnet-luis-dialogs.md b/articles/dotnet/bot-builder-dotnet-luis-dialogs.md deleted file mode 100644 index 285801752..000000000 --- a/articles/dotnet/bot-builder-dotnet-luis-dialogs.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -title: Recognize intents and entities with LUIS (v3 C#) - Bot Service -description: Learn how to enable your bot to understand natural language by using LUIS dialogs in the Bot Framework SDK for .NET. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - - -# Recognize intents and entities with LUIS - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -This article uses the example of a bot for taking notes, to demonstrate how Language Understanding ([LUIS][LUIS]) helps your bot respond appropriately to natural language input. A bot detects what a user wants to do by identifying their **intent**. This intent is determined from spoken or textual input, or **utterances**. The intent maps utterances to actions that the bot takes. For example, a note-taking bot recognizes a `Notes.Create` intent to invoke the functionality for creating a note. A bot may also need to extract **entities**, which are important words in utterances. In the example of a note-taking bot, the `Notes.Title` entity identifies the title of each note. - -## Create a Language Understanding bot with Bot Service - -1. In the [Azure portal](https://portal.azure.com), select **Create new resource** in the menu blade and click **See all**. - - ![Create new resource](../media/bot-builder-dotnet-use-luis/bot-service-creation.png) - -2. In the search box, search for **Web App Bot**. - - ![Create new resource](../media/bot-builder-dotnet-use-luis/bot-service-selection.png) - -3. In the **Bot Service** blade, provide the required information, and click **Create**. This creates and deploys the bot service and LUIS app to Azure. - * Set **App name** to your bot’s name. The name is used as the subdomain when your bot is deployed to the cloud (for example, mynotesbot.azurewebsites.net). This name is also used as the name of the LUIS app associated with your bot. Copy it to use later, to find the LUIS app associated with the bot. - * Select the subscription, [resource group](/azure/azure-resource-manager/resource-group-overview), App service plan, and [location](https://azure.microsoft.com/regions/). - * Select the **Language understanding (C#)** template for the **Bot template** field. - - ![Bot Service blade](../media/bot-builder-dotnet-use-luis/bot-service-setting-callout-template.png) - - * Check the box to confirm to the terms of service. - -4. Confirm that the bot service has been deployed. - * Click Notifications (the bell icon that is located along the top edge of the Azure portal). The notification will change from **Deployment started** to **Deployment succeeded**. - * After the notification changes to **Deployment succeeded**, click **Go to resource** on that notification. - -## Try the bot - -Confirm that the bot has been deployed by checking the **Notifications**. The notifications will change from **Deployment in progress...** to **Deployment succeeded**. Click **Go to resource** button to open the bot's resources blade. - -Once the bot is registered, click **Test in Web Chat** to open the Web Chat pane. Type "hello" in Web Chat. - - ![Test the bot in Web Chat](../media/bot-builder-dotnet-use-luis/bot-service-web-chat.png) - -The bot responds by saying "You have reached Greeting. You said: hello". This confirms that the bot has received your message and passed it to a default LUIS app that it created. This default LUIS app detected a Greeting intent. - -## Modify the LUIS app - -Log in to [https://www.luis.ai](https://www.luis.ai) using the same account you use to log in to Azure. Click on **My apps**. In the list of apps, find the app that begins with the name specified in **App name** in the **Bot Service** blade when you created the Bot Service. - -The LUIS app starts with 4 intents: Cancel: Greeting, Help, and None. - -The following steps add the Note.Create, Note.ReadAloud, and Note.Delete intents: - -1. Click on **Prebuit Domains** in the lower left of the page. Find the **Note** domain and click **Add domain**. - -2. This tutorial doesn't use all of the intents included in the **Note** prebuilt domain. In the **Intents** page, click on each of the following intent names and then click the **Delete Intent** button. - * Note.ShowNext - * Note.DeleteNoteItem - * Note.Confirm - * Note.Clear - * Note.CheckOffItem - * Note.AddToNote - - The only intents that should remain in the LUIS app are the following: - * Note.ReadAloud - * Note.Create - * Note.Delete - * None - * Help - * Greeting - * Cancel - - ![intents shown in LUIS app](../media/bot-builder-dotnet-use-luis/luis-intent-list.png) - -3. Click the **Train** button in the upper right to train your app. -4. Click **PUBLISH** in the top navigation bar to open the **Publish** page. Click the **Publish to production slot** button. After successful publish, copy the URL displayed in the **Endpoint** column the **Publish App** page, in the row that starts with the Resource Name Starter_Key. Save this URL to use later in your bot’s code. The URL has a format similar to this example: `https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?subscription-key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&timezoneOffset=0&verbose=true&q=` - -## Modify the bot code - -Click **Build** and then click **Open online code editor**. - ![Open online code editor](../media/bot-builder-dotnet-use-luis/bot-service-build.png) - -In the code editor, open `BasicLuisDialog.cs`. It contains the following code for handling intents from the LUIS app. -```cs -using System; -using System.Configuration; -using System.Threading.Tasks; - -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Luis; -using Microsoft.Bot.Builder.Luis.Models; - -namespace Microsoft.Bot.Sample.LuisBot -{ - // For more information about this template visit http://aka.ms/basic-luis-dialog - [Serializable] - public class BasicLuisDialog : LuisDialog - { - public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute( - ConfigurationManager.AppSettings["LuisAppId"], - ConfigurationManager.AppSettings["LuisAPIKey"], - domain: ConfigurationManager.AppSettings["LuisAPIHostName"]))) - { - } - - [LuisIntent("None")] - public async Task NoneIntent(IDialogContext context, LuisResult result) - { - await this.ShowLuisResult(context, result); - } - - // Go to https://luis.ai and create a new intent, then train/publish your luis app. - // Finally replace "Greeting" with the name of your newly created intent in the following handler - [LuisIntent("Greeting")] - public async Task GreetingIntent(IDialogContext context, LuisResult result) - { - await this.ShowLuisResult(context, result); - } - - [LuisIntent("Cancel")] - public async Task CancelIntent(IDialogContext context, LuisResult result) - { - await this.ShowLuisResult(context, result); - } - - [LuisIntent("Help")] - public async Task HelpIntent(IDialogContext context, LuisResult result) - { - await this.ShowLuisResult(context, result); - } - - private async Task ShowLuisResult(IDialogContext context, LuisResult result) - { - await context.PostAsync($"You have reached {result.Intents[0].Intent}. You said: {result.Query}"); - context.Wait(MessageReceived); - } - } -} -``` -### Create a class for storing notes - -Add the following `using` statement in BasicLuisDialog.cs. - -```cs -using System.Collections.Generic; -``` - -Add the following code within the `BasicLuisDialog` class, after the constructor definition. - -```cs - // Store notes in a dictionary that uses the title as a key - private readonly Dictionary noteByTitle = new Dictionary(); - - [Serializable] - public sealed class Note : IEquatable - { - - public string Title { get; set; } - public string Text { get; set; } - - public override string ToString() - { - return $"[{this.Title} : {this.Text}]"; - } - - public bool Equals(Note other) - { - return other != null - && this.Text == other.Text - && this.Title == other.Title; - } - - public override bool Equals(object other) - { - return Equals(other as Note); - } - - public override int GetHashCode() - { - return this.Title.GetHashCode(); - } - } - - // CONSTANTS - // Name of note title entity - public const string Entity_Note_Title = "Note.Title"; - // Default note title - public const string DefaultNoteTitle = "default"; -``` - -### Handle the Note.Create intent -To handle the Note.Create intent, add the following code to the `BasicLuisDialog` class. - -```cs - private Note noteToCreate; - private string currentTitle; - [LuisIntent("Note.Create")] - public Task NoteCreateIntent(IDialogContext context, LuisResult result) - { - EntityRecommendation title; - if (!result.TryFindEntity(Entity_Note_Title, out title)) - { - // Prompt the user for a note title - PromptDialog.Text(context, After_TitlePrompt, "What is the title of the note you want to create?"); - } - else - { - var note = new Note() { Title = title.Entity }; - noteToCreate = this.noteByTitle[note.Title] = note; - - // Prompt the user for what they want to say in the note - PromptDialog.Text(context, After_TextPrompt, "What do you want to say in your note?"); - } - - return Task.CompletedTask; - } - - - private async Task After_TitlePrompt(IDialogContext context, IAwaitable result) - { - EntityRecommendation title; - // Set the title (used for creation, deletion, and reading) - currentTitle = await result; - if (currentTitle != null) - { - title = new EntityRecommendation(type: Entity_Note_Title) { Entity = currentTitle }; - } - else - { - // Use the default note title - title = new EntityRecommendation(type: Entity_Note_Title) { Entity = DefaultNoteTitle }; - } - - // Create a new note object - var note = new Note() { Title = title.Entity }; - // Add the new note to the list of notes and also save it in order to add text to it later - noteToCreate = this.noteByTitle[note.Title] = note; - - // Prompt the user for what they want to say in the note - PromptDialog.Text(context, After_TextPrompt, "What do you want to say in your note?"); - - } - - private async Task After_TextPrompt(IDialogContext context, IAwaitable result) - { - // Set the text of the note - noteToCreate.Text = await result; - - await context.PostAsync($"Created note **{this.noteToCreate.Title}** that says \"{this.noteToCreate.Text}\"."); - - context.Wait(MessageReceived); - } -``` - -### Handle the Note.ReadAloud Intent -The bot can use the `Note.ReadAloud` intent to show the contents of a note, or of all the notes if the note title isn't detected. - -Paste the following code into the `BasicLuisDialog` class. -```cs - [LuisIntent("Note.ReadAloud")] - public async Task NoteReadAloudIntent(IDialogContext context, LuisResult result) - { - Note note; - if (TryFindNote(result, out note)) - { - await context.PostAsync($"**{note.Title}**: {note.Text}."); - } - else - { - // Print out all the notes if no specific note name was detected - string NoteList = "Here's the list of all notes: \n\n"; - foreach (KeyValuePair entry in noteByTitle) - { - Note noteInList = entry.Value; - NoteList += $"**{noteInList.Title}**: {noteInList.Text}.\n\n"; - } - await context.PostAsync(NoteList); - } - - context.Wait(MessageReceived); - } - - public bool TryFindNote(string noteTitle, out Note note) - { - bool foundNote = this.noteByTitle.TryGetValue(noteTitle, out note); // TryGetValue returns false if no match is found. - return foundNote; - } - - public bool TryFindNote(LuisResult result, out Note note) - { - note = null; - - string titleToFind; - - EntityRecommendation title; - if (result.TryFindEntity(Entity_Note_Title, out title)) - { - titleToFind = title.Entity; - } - else - { - titleToFind = DefaultNoteTitle; - } - - return this.noteByTitle.TryGetValue(titleToFind, out note); // TryGetValue returns false if no match is found. - } -``` - -### Handle the Note.Delete intent -Paste the following code into the `BasicLuisDialog` class. - -```cs - [LuisIntent("Note.Delete")] - public async Task NoteDeleteIntent(IDialogContext context, LuisResult result) - { - Note note; - if (TryFindNote(result, out note)) - { - this.noteByTitle.Remove(note.Title); - await context.PostAsync($"Note {note.Title} deleted"); - } - else - { - // Prompt the user for a note title - PromptDialog.Text(context, After_DeleteTitlePrompt, "What is the title of the note you want to delete?"); - } - } - - private async Task After_DeleteTitlePrompt(IDialogContext context, IAwaitable result) - { - Note note; - string titleToDelete = await result; - bool foundNote = this.noteByTitle.TryGetValue(titleToDelete, out note); - - if (foundNote) - { - this.noteByTitle.Remove(note.Title); - await context.PostAsync($"Note {note.Title} deleted"); - } - else - { - await context.PostAsync($"Did not find note named {titleToDelete}."); - } - - context.Wait(MessageReceived); - } -``` - -## Build the bot -Right-click on **build.cmd** in the code editor and choose **Run from Console**. - - ![Run build.cmd](../media/bot-builder-dotnet-use-luis/bot-service-run-console.png) - -## Test the bot - -In the Azure Portal, click on **Test in Web Chat** to test the bot. Try type messages like "Create a note", "read my notes", and "delete notes". - ![Test notes bot in Web Chat](../media/bot-builder-dotnet-use-luis/bot-service-test-notebot.png) - -> [!TIP] -> If you find that your bot doesn't always recognize the correct intent or entities, improve your LUIS app's performance by giving it more example utterances to train it. You can retrain your LUIS app without any modification to your bot's code. See [Add example utterances](/azure/cognitive-services/LUIS/add-example-utterances) and [train and test your LUIS app](/azure/cognitive-services/LUIS/train-test). - -> [!TIP] -> If your bot code runs into an issue, check the following: -> * You have [built the bot](./bot-builder-dotnet-luis-dialogs.md#build-the-bot). -> * Your bot code defines a handler for every intent in your LUIS app. - -## Next steps - -From trying the bot, you can see how tasks are invoked by a LUIS intent. However, this simple example doesn't allow for interruption of the currently active dialog. Allowing and handling interruptions like "help" or "cancel" is a flexible design that accounts for what users really do. Learn more about using scorable dialogs so that your dialogs can handle interruptions. - -> [!div class="nextstepaction"] -> [Global message handlers using scorables](bot-builder-dotnet-scorable-dialogs.md) - -## Additional resources - -- [Dialogs](bot-builder-dotnet-dialogs.md) -- [Manage conversation flow with dialogs](bot-builder-dotnet-manage-conversation-flow.md) -- LUIS -- Bot Framework SDK for .NET Reference - -[LUIS]: https://www.luis.ai/ -[NotesSample]: https://github.com/Microsoft/BotFramework-Samples/tree/master/docs-samples/CSharp/Simple-LUIS-Notes-Sample -[NotesSampleJSON]: https://github.com/Microsoft/BotFramework-Samples/blob/master/docs-samples/CSharp/Simple-LUIS-Notes-Sample/Notes.json diff --git a/articles/dotnet/bot-builder-dotnet-manage-conversation-flow.md b/articles/dotnet/bot-builder-dotnet-manage-conversation-flow.md deleted file mode 100644 index 94800ecaf..000000000 --- a/articles/dotnet/bot-builder-dotnet-manage-conversation-flow.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Manage conversation flow with dialogs - Bot Service -description: Learn how to model conversations and manage conversation flow using dialogs and the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' - ---- - -# Manage conversation flow with dialogs - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-manage-conversation-flow.md) -> - [Node.js](../nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md) - -[!INCLUDE [Dialog flow example](../includes/snippet-dotnet-manage-conversation-flow-intro.md)] - -This article describes how to model this conversation flow by using [dialogs](bot-builder-dotnet-dialogs.md) and the Bot Framework SDK for .NET. - -## Invoke the root dialog - -First, the bot controller invokes the "root dialog". -The following example shows how to wire the basic HTTP POST call to a controller and then invoke the root dialog. - -```cs -public class MessagesController : ApiController -{ - public async Task Post([FromBody]Activity activity) - { - // Redirect to the root dialog. - await Conversation.SendAsync(activity, () => new RootDialog()); - ... - } -} -``` - -## Invoke the 'New Order' dialog - -Next, the root dialog invokes the 'New Order' dialog. - -```cs -[Serializable] -public class RootDialog : IDialog -{ - public async Task StartAsync(IDialogContext context) - { - // Root dialog initiates and waits for the next message from the user. - // When a message arrives, call MessageReceivedAsync. - context.Wait(this.MessageReceivedAsync); - } - - public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) - { - var message = await result; // We've got a message! - if (message.Text.ToLower().Contains("order")) - { - // User said 'order', so invoke the New Order Dialog and wait for it to finish. - // Then, call ResumeAfterNewOrderDialog. - await context.Forward(new NewOrderDialog(), this.ResumeAfterNewOrderDialog, message, CancellationToken.None); - } - // User typed something else; for simplicity, ignore this input and wait for the next message. - context.Wait(this.MessageReceivedAsync); - } - - private async Task ResumeAfterNewOrderDialog(IDialogContext context, IAwaitable result) - { - // Store the value that NewOrderDialog returned. - // (At this point, new order dialog has finished and returned some value to use within the root dialog.) - var resultFromNewOrder = await result; - - await context.PostAsync($"New order dialog just told me this: {resultFromNewOrder}"); - - // Again, wait for the next message from the user. - context.Wait(this.MessageReceivedAsync); - } -} -``` - -## Dialog lifecycle - -When a dialog is invoked, it takes control of the conversation flow. -Every new message will be subject to processing by that dialog until it either closes or redirects to another dialog. - -In C#, you can use `context.Wait()` to specify the callback to invoke the next time the user sends a message. -To close a dialog and remove it from the stack (thereby sending the user back to the prior dialog in the stack), use `context.Done()`. -You must end every dialog method with `context.Wait()`, `context.Fail()`, `context.Done()`, -or some redirection directive such as `context.Forward()` or `context.Call()`. -A dialog method that does not end with one of these will result in an error -(because the framework does not know what action to take the next time the user sends a message). - -## Passing state between dialogs - -While you can store state in bot state, you can also pass data between different dialogs by overloading your dialog class constructor. - -```cs -[Serializable] -public class AgeDialog : IDialog -{ - private string name; - - public AgeDialog(string name) - { - this.name = name; - } -} - ``` - -Calling dialog code, passing in name value from the user. - -```cs -private async Task NameDialogResumeAfter(IDialogContext context, IAwaitable result) -{ - try - { - this.name = await result; - context.Call(new AgeDialog(this.name), this.AgeDialogResumeAfter); - } - catch (TooManyAttemptsException) - { - await context.PostAsync("I'm sorry, I'm having issues understanding you. Let's try again."); - await this.SendWelcomeMessageAsync(context); - } -} -``` - -## Sample code - -For a complete sample that shows how to manage a conversation by using dialogs in the Bot Framework SDK for .NET, see the [Basic Multi-Dialog sample](https://aka.ms/v3cs-MultiDialog-Sample) in GitHub. - -## Additional resources - -- [Dialogs](bot-builder-dotnet-dialogs.md) -- [Design and control conversation flow](../bot-service-design-conversation-flow.md) -- [Basic Multi-Dialog sample (GitHub)](https://aka.ms/v3cs-MultiDialog-Sample) -- Bot Framework SDK for .NET Reference diff --git a/articles/dotnet/bot-builder-dotnet-middleware.md b/articles/dotnet/bot-builder-dotnet-middleware.md deleted file mode 100644 index 10632c945..000000000 --- a/articles/dotnet/bot-builder-dotnet-middleware.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Intercept messages (v3 C#) - Bot Service -description: Learn how to intercept messages between user and bot using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 03/01/2019 -monikerRange: 'azure-bot-service-3.0' ---- -# Intercept messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-middleware.md) -> - [Node.js](../nodejs/bot-builder-nodejs-intercept-messages.md) - -[!INCLUDE [Introducton to message logging](../includes/snippet-message-logging-intro.md)] - -## Intercept and log messages - -The following code sample shows how to intercept messages that are exchanged between user and bot -using the concept of **middleware** in the Bot Framework SDK for .NET. - -First, create a `DebugActivityLogger` class and define a `LogAsync` method to specify what action is taken for each intercepted message. This example just prints some information about each message. - -```cs -public class DebugActivityLogger : IActivityLogger -{ - public async Task LogAsync(IActivity activity) - { - Debug.WriteLine($"From:{activity.From.Id} - To:{activity.Recipient.Id} - Message:{activity.AsMessageActivity()?.Text}"); - } -} -``` - -Then, add the following code to `Global.asax.cs`. Every message that is exchanged between user and bot (in either direction) will now trigger the `LogAsync` method in the `DebugActivityLogger` class. - -```cs - public class WebApiApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - var builder = new ContainerBuilder(); - builder.RegisterType().AsImplementedInterfaces().InstancePerDependency(); - builder.Update(Conversation.Container); - - GlobalConfiguration.Configure(WebApiConfig.Register); - } - } -``` - -Although this example simply prints some information about each message, you can update the `LogAsync` method to specify the actions that you want to take for each message. - -## Sample code - -For a complete sample that shows how to intercept and log messages using the Bot Framework SDK for .NET, see the Middleware sample in GitHub. - -## Additional resources - -- Bot Framework SDK for .NET Reference -- Middleware sample (GitHub) diff --git a/articles/dotnet/bot-builder-dotnet-overview.md b/articles/dotnet/bot-builder-dotnet-overview.md deleted file mode 100644 index 46a503bf4..000000000 --- a/articles/dotnet/bot-builder-dotnet-overview.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Bot Framework SDK for .NET - Bot Service -description: Get started with Bot Framework SDK for .NET, a powerful, easy-to-use framework for building bots. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Bot Framework SDK for .NET - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-overview.md) -> - [Node.js](../nodejs/bot-builder-nodejs-overview.md) -> - [REST](../rest-api/bot-framework-rest-overview.md) - -The Bot Framework SDK for .NET is a powerful framework for constructing bots that can handle both free-form interactions -and more guided conversations where the user selects from possible values. -It is easy to use and leverages C# to provide a familiar way for .NET developers to write bots. - -Using the SDK, you can build bots that take advantage of the following SDK features: - -- Powerful dialog system with dialogs that are isolated and composable -- Built-in prompts for simple things such as Yes/No, strings, numbers, and enumerations -- Built-in dialogs that utilize powerful AI frameworks such as LUIS -- FormFlow for automatically generating a bot (from a C# class) that guides the user through the -conversation, providing help, navigation, clarification, and confirmation as needed - -> [!IMPORTANT] -> On July 31, 2017 breaking changes have been implemented in the Bot Framework security protocol. -> To prevent these changes from adversely impacting your bot, you must ensure that your application is -> using Bot Framework SDK v3.5 or greater. If you've built a bot using an -> SDK that you obtained prior to January 5, 2017 (the release date for Bot Framework SDK v3.5), -> be sure to upgrade to Bot Framework SDK v3.5 or later. - -## Get the SDK - -The SDK is available as a NuGet package and as open source on GitHub. - -> [!IMPORTANT] -> The Bot Framework SDK for .NET requires .NET Framework 4.6 or newer. If you are adding the SDK to an existing project -> targeting a lower version of the .NET Framework, you will need to update your project to target .NET Framework 4.6 first. - -To install the SDK within a Visual Studio project, complete the following steps: - -1. In **Solution Explorer**, right-click the project name and select **Manage NuGet Packages...**. -2. On the **Browse** tab, type "Microsoft.Bot.Builder" into the search box. -3. Select **Microsoft.Bot.Builder** in the list of results, click **Install**, and accept the changes. - -## Get code samples - -This SDK includes [sample source code](bot-builder-dotnet-samples.md) that uses the features of the Bot Framework SDK for .NET. - -## Next steps - -Learn more about building bots using the Bot Framework SDK for .NET by reviewing articles throughout this section, beginning with: - -- [Quickstart](bot-builder-dotnet-quickstart.md): Quickly build and test a simple bot by following instructions in this step-by-step tutorial. -- [Key concepts](bot-builder-dotnet-concepts.md): Learn about key concepts in the Bot Framework SDK for .NET. - -If you encounter problems or have suggestions regarding the Bot Framework SDK for .NET, see [Support](../bot-service-resources-links-help.md) for a list of available resources. diff --git a/articles/dotnet/bot-builder-dotnet-proactive-messages.md b/articles/dotnet/bot-builder-dotnet-proactive-messages.md deleted file mode 100644 index cf757a4fc..000000000 --- a/articles/dotnet/bot-builder-dotnet-proactive-messages.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: Send proactive messages (v3 C#) - Bot Service -description: Learn how to send proactive messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Send proactive messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-proactive-messages.md) -> - [Node.js](../nodejs/bot-builder-nodejs-proactive-messages.md) - -[!INCLUDE [Introduction to proactive messages - part 1](../includes/snippet-proactive-messages-intro-1.md)] - -## Types of proactive messages - -[!INCLUDE [Introduction to proactive messages - part 2](../includes/snippet-proactive-messages-intro-2.md)] - -## Send an ad hoc proactive message - -The following code samples show how to send an ad hoc proactive message with the Bot Framework SDK for .NET. - -To be able to send an ad hoc message to a user, the bot must first collect and store some information about the user from the current conversation. - -```cs -public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) -{ - var message = await result; - - // Extract data from the user's message that the bot will need later to send an ad hoc message to the user. - // Store the extracted data in a custom class "ConversationStarter" (not shown here). - - ConversationStarter.toId = message.From.Id; - ConversationStarter.toName = message.From.Name; - ConversationStarter.fromId = message.Recipient.Id; - ConversationStarter.fromName = message.Recipient.Name; - ConversationStarter.serviceUrl = message.ServiceUrl; - ConversationStarter.channelId = message.ChannelId; - ConversationStarter.conversationId = message.Conversation.Id; - - // (Save this information somewhere that it can be accessed later, such as in a database.) - - await context.PostAsync("Hello user, good to meet you! I now know your address and can send you notifications in the future."); - context.Wait(MessageReceivedAsync); -} -``` -> [!NOTE] -> For simplicity, this example does not specify how to store the user data. It does not matter how the data is -> stored as long as the bot can retrieve it later. - -Now that the data has been stored, the bot can simply retrieve the data, construct the ad hoc proactive message, and send it. - -```cs -// Use the data stored previously to create the required objects. -var userAccount = new ChannelAccount(toId,toName); -var botAccount = new ChannelAccount(fromId, fromName); -var connector = new ConnectorClient(new Uri(serviceUrl)); - -// Create a new message. -IMessageActivity message = Activity.CreateMessageActivity(); -if (!string.IsNullOrEmpty(conversationId) && !string.IsNullOrEmpty(channelId)) -{ - // If conversation ID and channel ID was stored previously, use it. - message.ChannelId = channelId; -} -else -{ - // Conversation ID was not stored previously, so create a conversation. - // Note: If the user has an existing conversation in a channel, this will likely create a new conversation window. - conversationId = (await connector.Conversations.CreateDirectConversationAsync( botAccount, userAccount)).Id; -} - -// Set the address-related properties in the message and send the message. -message.From = botAccount; -message.Recipient = userAccount; -message.Conversation = new ConversationAccount(id: conversationId); -message.Text = "Hello, this is a notification"; -message.Locale = "en-us"; -await connector.Conversations.SendToConversationAsync((Activity)message); -``` - -> [!NOTE] -> If the bot specifies a conversation ID that was stored previously, the message will likely be delivered to the user in the existing conversation window on the client. -> If the bot generates a new conversation ID, the message will be delivered to the user in a new conversation window on the client, provided that the client supports multiple conversation windows. - -## Send a dialog-based proactive message - -The following code samples show how to send a dialog-based proactive message by using the Bot Framework SDK for .NET. - -To be able to send a dialog-based proactive message to a user, the bot must first collect and save information from the current conversation. - -```cs -public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) -{ - var message = await result; - - // Store information about this specific point the conversation, so that the bot can resume this conversation later. - var conversationReference = message.ToConversationReference(); - ConversationStarter.conversationReference = JsonConvert.SerializeObject(conversationReference); - - await context.PostAsync("Greetings, user! I now know how to start a proactive message to you."); - context.Wait(MessageReceivedAsync); -} -``` - -When it is time to send the message, the bot creates a new dialog and adds it to the top of the dialog stack. The new dialog takes control of the conversation, delivers the proactive message, closes, and then returns control to the previous dialog in the stack. - -```cs -// This will interrupt the conversation and send the user to SurveyDialog, then wait until that's done -public static async Task Resume() -{ - // Recreate the message from the conversation reference that was saved previously. - var message = JsonConvert.DeserializeObject(conversationReference).GetPostToBotMessage(); - var client = new ConnectorClient(new Uri(message.ServiceUrl)); - - // Create a scope that can be used to work with state from bot framework. - using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) - { - var botData = scope.Resolve(); - await botData.LoadAsync(CancellationToken.None); - - // This is our dialog stack. - var task = scope.Resolve(); - - // Create the new dialog and add it to the stack. - var dialog = new SurveyDialog(); - // interrupt the stack. This means that we're stopping whatever conversation that is currently happening with the user - // Then adding this stack to run and once it's finished, we will be back to the original conversation - task.Call(dialog.Void(), null); - - await task.PollAsync(CancellationToken.None); - - // Flush the dialog stack back to its state store. - await botData.FlushAsync(CancellationToken.None); - } -} -``` -The `SurveyDialog` controls the conversation until it finishes. When its task is finished, it calls `context.Done` and closes, returning control to the previous dialog. - -```cs -[Serializable] -public class SurveyDialog : IDialog -{ - public async Task StartAsync(IDialogContext context) - { - await context.PostAsync("Hello, I'm the survey dialog. I'm interrupting your conversation to ask you a question. Type \"done\" to resume"); - - context.Wait(this.MessageReceivedAsync); - } - public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) - { - if ((await result).Text == "done") - { - await context.PostAsync("Great, back to the original conversation!"); - context.Done(String.Empty); // Finish this dialog. - } - else - { - await context.PostAsync("I'm still on the survey until you type \"done\""); - context.Wait(MessageReceivedAsync); // Not done yet. - } - } -} -``` - -## Sample code - -For a complete sample that shows how to send proactive messages using the Bot Framework SDK for .NET, see the Proactive Messages sample in GitHub. -Within the Proactive Messages sample, simpleSendMessage shows how to send an ad-hoc proactive message and startNewDialog shows how to send a dialog-based proactive message. - -## Additional resources - -- [Design and control conversation flow](../bot-service-design-conversation-flow.md) -- Bot Framework SDK for .NET Reference -- Proactive Messages sample (GitHub) - diff --git a/articles/dotnet/bot-builder-dotnet-request-payment.md b/articles/dotnet/bot-builder-dotnet-request-payment.md deleted file mode 100644 index 14dafcddd..000000000 --- a/articles/dotnet/bot-builder-dotnet-request-payment.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: Request payment (v3 C#) - Bot Service -description: Learn how to send a payment request using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Request payment - -> [!NOTE] -> The Payment service referred to in this article will be deprecated on December 1, 2019. - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-request-payment.md) -> - [Node.js](../nodejs/bot-builder-nodejs-request-payment.md) - -If your bot enables users to purchase items, it can request payment by including -a special type of button within a [rich card](bot-builder-dotnet-add-rich-card-attachments.md). -This article describes how to send a payment request using the Bot Framework SDK for .NET. - -## Prerequisites - -Before you can send a payment request using the Bot Framework SDK for .NET, you must complete these prerequisite tasks. - -### Update Web.config - -Update your bot's **Web.config** file to set `MicrosoftAppId` and `MicrosoftAppPassword` to the app ID and password values that were generated for your bot during the [registration](~/bot-service-quickstart-registration.md) process. - -> [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -### Create and configure merchant account - -1. Create and activate a Stripe account if you don't have one already. - -2. Sign in to Seller Center with your Microsoft account. - -3. Within Seller Center, connect your account with Stripe. - -4. Within Seller Center, navigate to the Dashboard and copy the value of **MerchantID**. - -5. Update your bot's **Web.config** file to set `MerchantId` to the value that you copied from the Seller Center Dashboard. - -[!INCLUDE [Payment process overview](../includes/snippet-payment-process-overview.md)] - -## Payment Bot sample - -The Payment Bot sample provides an example of a bot that sends a payment request -using .NET. -To see this sample bot in action, you can -try it out in web chat, -add it as a Skype -contact or download the payment bot sample and run it locally using the Bot Framework Emulator. - -> [!NOTE] -> To complete the end-to-end payment process using the **Payment Bot** sample in web chat or Skype, -> you must specify a valid credit card or debit card within your Microsoft account -> (i.e., a valid card from a U.S. card issuer). -> Your card will not be charged and the card's CVV will not be verified, -> because the **Payment Bot** sample runs in test mode (i.e., `LiveMode` is set to `false` in **Web.config**). - -The next few sections of this article describe the three parts of the payment process, -in the context of the **Payment Bot** sample. - -## Requesting payment - -Your bot can request payment from a user by sending a message that contains a -[rich card attachment](bot-builder-dotnet-add-rich-card-attachments.md) with a button that specifies -`CardAction.Type` of "payment". -This code snippet from the **Payment Bot** sample creates a message that contains a Hero card with a **Buy** button that the user can click (or tap) to initiate the payment process. - -[!code-csharp[Request payment](../includes/code/dotnet-request-payment.cs#requestPayment)] - -In this example, the button's type is specified as `PaymentRequest.PaymentActionType`, which -the Bot Builder library defines as "payment". -The button's value is populated by the `BuildPaymentRequest` method, which returns -a `PaymentRequest` object that contains information about supported payment methods, details, -and options. -For more information about implementation details, see **Dialogs/RootDialog.cs** within the -Payment Bot sample. - -This screenshot shows the Hero card (with **Buy** button) that's generated by the code snippet above. - -![Payments sample bot](../media/payments-bot-buy.png) - -> [!IMPORTANT] -> Any user that has access to the **Buy** button may use it to initiate the payment process. -> Within the context of a group conversation, it is not possible to designate a button for use by only -> a specific user. - -## User experience - -When a user clicks the **Buy** button, he or she is directed to a payment web experience to provide all required payment, shipping, and contact information via their Microsoft account. - -![Microsoft payment](../media/microsoft-payment.png) - -### HTTP callbacks - -HTTP callbacks will be sent to your bot to indicate that it should perform certain operations. -Each callback will be an [activity](bot-builder-dotnet-activities.md) -that contains these property values: - -| Property | Value | -|----|----| -| `Activity.Type` | invoke | -| `Activity.Name` | Indicates the type of operation that the bot should perform (e.g., shipping address update, shipping option update, payment complete). | -| `Activity.Value` | The request payload in JSON format. | -| `Activity.RelatesTo` | Describes the channel and user that are associated with the payment request. | - -> [!NOTE] -> `invoke` is a special activity type that is reserved for use by the Microsoft Bot Framework. -> The sender of an `invoke` activity will expect your bot to acknowledge the callback by sending an HTTP response. - -## Processing callbacks - -[!INCLUDE [Process callbacks overview](../includes/snippet-payment-process-callbacks-overview.md)] - -### Shipping Address Update and Shipping Option Update callbacks - -[!INCLUDE [Process shipping address and shipping option callbacks](../includes/snippet-payment-process-callbacks-1.md)] - -### Payment Complete callbacks - -When receiving a Payment Complete callback, your bot will be provided with a copy of the initial, unmodified payment request as -well as the payment response objects in the `Activity.Value` property. The payment response object will contain the final selections -made by the customer along with a payment token. Your bot should take the opportunity to recalculate the final payment request based on -the initial payment request and the customer's final selections. Assuming the customer's selections are determined to be valid, the bot -should verify the amount and currency in the payment token header to ensure that they match the final payment request. If the bot -decides to charge the customer it should only charge the amount in the payment token header as this is the price the customer confirmed. -If there is a mismatch between the values that the bot expects and the values that it received in the Payment Complete callback, it can -fail the payment request by sending HTTP status code `200 OK` along with setting the result field to `failure`. - -In addition to verifying payment details, the bot should also verify that the order can be fulfilled, before it initiates payment -processing. For example, it may want to verify that the item(s) being purchased are still available in stock. -If the values are correct and your payment processor has successfully charged the payment token, then the bot should respond with HTTP -status code `200 OK` along with setting the result field to `success` in order for the payment web experience to display the payment -confirmation. The payment token that the bot receives can only be used once, by the merchant that requested it, and must be submitted to -Stripe (the only payment processor that the Bot Framework currently supports). Sending any HTTP status code in the `400` or `500` range -to will result in a generic error for the customer. - -The `OnInvoke` method in the **Payment Bot** sample processes the callbacks that the bot receives. - -[!code-csharp[Request payment](../includes/code/dotnet-request-payment.cs#processCallback)] - -In this example, the bot examines the `Name` property of the incoming activity to identify the type of -operation it needs to perform, and then calls the appropriate method to process the callback. -For more information about implementation details, see **Controllers/MessagesControllers.cs** -within the Payment Bot sample. - -## Testing a payment bot - -[!INCLUDE [Test a payment bot](../includes/snippet-payment-test-bot.md)] - -In the Payment Bot sample, the `LiveMode` configuration setting in **Web.config** determines whether -Payment Complete callbacks will contain emulated payment tokens or real payment tokens. If `LiveMode` is set to `false`, a header is added to the bot's outbound payment request to indicate that the bot is in test mode, and the Payment Complete callback will contain an emulated payment token that cannot be charged. If `LiveMode` is set to `true`, the header which indicates that the bot is in test mode is omitted from the bot's outbound payment request, and the Payment Complete callback will contain a real payment token that the bot will submit to Stripe for payment processing. This will be a real transaction that results in charges to the specified payment instrument. - -## Additional resources - -- Payment Bot sample -- [Activities overview](bot-builder-dotnet-activities.md) -- [Add rich cards to messages](bot-builder-dotnet-add-rich-card-attachments.md) -- Web Payments at W3C -- Bot Framework SDK for .NET Reference diff --git a/articles/dotnet/bot-builder-dotnet-scorable-dialogs.md b/articles/dotnet/bot-builder-dotnet-scorable-dialogs.md deleted file mode 100644 index d4465b0d1..000000000 --- a/articles/dotnet/bot-builder-dotnet-scorable-dialogs.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: Global message handlers using scorables -description: Create more flexible dialogs using scorables within the Bot Framework SDK for .NET. -author: matthewshim-ms -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Global message handlers using scorables - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Users attempt to access certain functionality within a bot by using words like "help," "cancel," or "start over" in the middle of a conversation when the bot is expecting a different response. You can design your bot to gracefully handle such requests using scorable dialogs. - -Scorable dialogs monitor all incoming messages and determine whether a message is actionable in some way. Messages that are scorable are assigned a score between [0 – 1] by each scorable dialog. The scorable dialog that determines the highest score is added to the top of the dialog stack and then hands the response to the user. After the scorable dialog completes execution, the conversation continues from where it left off. - -Scorables enable you to create more flexible conversations by allowing your users to 'interrupt' the normal conversation flow you find in regular dialogs. - -## Create a scorable dialog - -First, define a new [dialog](bot-builder-dotnet-dialogs.md). The following code uses a dialog that is derived from the `IDialog` interface. - -```cs -public class SampleDialog : IDialog -{ - public async Task StartAsync(IDialogContext context) - { - await context.PostAsync("This is a Sample Dialog which is Scorable. Reply with anything to return to the prior prior dialog."); - - context.Wait(this.MessageReceived); - } - - private async Task MessageReceived(IDialogContext context, IAwaitable result) - { - var message = await result; - - if ((message.Text != null) && (message.Text.Trim().Length > 0)) - { - context.Done(null); - } - else - { - context.Fail(new Exception("Message was not a string or was an empty string.")); - } - } -} -``` -To make a scorable dialog, create a class that inherits from the `ScorableBase` abstract class. The following code shows a `SampleScorable` class. - -```cs -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Internals; -using Microsoft.Bot.Builder.Internals.Fibers; -using Microsoft.Bot.Builder.Scorables.Internals; - -public class SampleScorable : ScorableBase -{ - private readonly IDialogTask task; - - public SampleScorable(IDialogTask task) - { - SetField.NotNull(out this.task, nameof(task), task); - } -} -``` -The `ScorableBase` abstract class inherits from the `IScorable` interface. You will need to implement the following `IScorable` methods in your class: - -- `PrepareAsync` is the first method that is called in the scorable instance. It accepts incoming message activity, analyzes and sets the dialog's state, which is passed to all the other methods of the `IScorable` interface. - -```cs -protected override async Task PrepareAsync(IActivity item, CancellationToken token) -{ - // TODO: insert your code here -} -``` - -- The `HasScore` method checks the state property to determine if the scorable dialog should provide a score for the message. If it returns false, the message will be ignored by the scorable dialog. - -```cs -protected override bool HasScore(IActivity item, string state) -{ - // TODO: insert your code here -} -``` - -- `GetScore` will only trigger if `HasScore` returns true. You’ll provision the logic in this method to determine the score for a message between 0 - 1. - -```cs -protected override double GetScore(IActivity item, string state) -{ - // TODO: insert your code here -} -``` -- In the `PostAsync` method, define core actions to be performed for the scorable class. All scorable dialogs will monitor incoming messages, and assign scores to valid messages based on the scorables' GetScore method. The scorable class which determines the highest score (between 0 - 1.0) will then trigger that scorable's `PostAsync` method. - -```cs -protected override Task PostAsync(IActivity item, string state, CancellationToken token) -{ - //TODO: insert your code here -} -``` - -- `DoneAsync` is called after the scoring process is complete. Use this method to dispose of any scoped resources. - -```cs -protected override Task DoneAsync(IActivity item, string state, CancellationToken token) -{ - //TODO: insert your code here -} -``` - -## Create a module to register the IScorable service - -Next, define a `Module` that will register the `SampleScorable` class as a component. This will provision the `IScorable` service. - -```cs -public class GlobalMessageHandlersBotModule : Module -{ - protected override void Load(ContainerBuilder builder) - { - base.Load(builder); - - builder - .Register(c => new SampleScorable(c.Resolve())) - .As>() - .InstancePerLifetimeScope(); - } -} -``` -## Register the module - -The last step in the process is to apply the `SampleScorable` to the bot's Conversation Container. This will register the scorable service within the Bot Framework's message handling pipeline. The following code shows to update the `Conversation.Container` within the bot app's initialization in **Global.asax.cs**: - -```cs -public class WebApiApplication : System.Web.HttpApplication -{ - protected void Application_Start() - { - this.RegisterBotModules(); - GlobalConfiguration.Configure(WebApiConfig.Register); - } - - private void RegisterBotModules() - { - var builder = new ContainerBuilder(); - builder.RegisterModule(new ReflectionSurrogateModule()); - - //Register the module within the Conversation container - builder.RegisterModule(); - - builder.Update(Conversation.Container); - } -} -``` - -## Additional resources -* [Global Message Handlers sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-GlobalMessageHandlers) -* [Simple Scorable Bot sample](https://github.com/Microsoft/BotFramework-Samples/tree/master/blog-samples/CSharp/ScorableBotSample) -* [Dialogs overview](bot-builder-dotnet-dialogs.md) -* [AutoFac](https://autofac.org/) diff --git a/articles/dotnet/bot-builder-dotnet-sdk-quickstart.md b/articles/dotnet/bot-builder-dotnet-sdk-quickstart.md deleted file mode 100644 index e6e795f1b..000000000 --- a/articles/dotnet/bot-builder-dotnet-sdk-quickstart.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Create a bot with the Bot Framework SDK for .NET - Bot Service -description: Create a bot with the Bot Framework SDK for .NET, a powerful bot construction framework. -keywords: Bot Framework SDK, create a bot, quickstart, .NET, getting started, C# bot -author: kamrani -ms.author: kamrani -manager: kamrani -ms.topic: conceptual -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - - -# Create a bot with the Bot Framework SDK for .NET - -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] - -This quickstart walks you through building a bot by using the C# template, and then testing it with the Bot Framework Emulator. - -[!INCLUDE [Azure vs local development](~/includes/snippet-quickstart-paths.md)] - -[!INCLUDE [dotnet quickstart](~/includes/quickstart-dotnet.md)] - -## Additional resources - -See [tunneling (ngrok)](https://github.com/Microsoft/BotFramework-Emulator/wiki/Tunneling-(ngrok)) for how to connect to a bot hosted remotely. - -## Next steps - -> [!div class="nextstepaction"] -> [Deploy your bot to Azure](../bot-builder-deploy-az-cli.md) - diff --git a/articles/dotnet/bot-builder-dotnet-search-azure.md b/articles/dotnet/bot-builder-dotnet-search-azure.md deleted file mode 100644 index a9f92036b..000000000 --- a/articles/dotnet/bot-builder-dotnet-search-azure.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Create data-driven experiences with Azure Search (v3 C#) - Bot Service -description: Learn how to create data-driven experiences with Azure Search and help users navigate large amounts of content in a bot with the Bot Framework SDK for .NET and Azure Search. -author: matthewshim-ms -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 1/28/2019 -monikerRange: 'azure-bot-service-3.0' ---- - -# Create data-driven experiences with Azure Search - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-search-azure.md) -> - [Node.js](../nodejs/bot-builder-nodejs-search-azure.md) - -You can add [Azure Search](https://azure.microsoft.com/services/search/) to a bot to help users navigate large amounts of content and create a data-driven exploration experience. - -Azure Search is an Azure service that offers keyword search, built-in linguistics, custom scoring, faceted navigation, and more. Azure Search can also index content from various sources, including Azure SQL DB, DocumentDB, Blob Storage, and Table Storage. It supports "push" indexing for other sources of data, and it can open PDFs, Office documents, and other formats containing unstructured data. Once collected, the content goes into an Azure Search index, which the bot can then query. - -## Prerequisites - -Install the [Microsoft.Azure.Search](https://www.nuget.org/packages/Microsoft.Azure.Search/4.0.0-preview) Nuget package in your bot project. - -The following three C# projects are required in your bot's solution. These projects provide additional functionality for bots and Azure Search. Fork the projects from [GitHub](https://aka.ms/v3-cs-search-demo) or download the source code directly. - -- The **Search.Azure** project defines the Azure Service call. -- The **Search.Contracts** project defines generic interfaces and data models to handle data. -- The **Search.Dialogs** project includes various generic Bot Builder dialogs used to query Azure Search. - -## Configure Azure Search settings - -Configure the Azure Search settings in the **Web.config** file of the project using your own Azure Search credentials in the value fields. -The constructor in the `AzureSearchClient` class will use these settings to register and bind the bot to the Azure Service. - -```xml - - - - - -``` - -## Create a search dialog - -In your bot's project, create a new `AzureSearchDialog` class to call the Azure Service in your bot. This new class must inherit the `SearchDialog` class from the -**Search.Dialogs** project, which handles most of the heavy lifting. The `GetTopRefiners()` override allows users to narrow/filter their search results without having to start the search over form the beginning, maintaining the search object's state. You can add your own custom refiners in the `TopRefiners` array to let your users filter or narrow down their search results. - -```cs -[Serializable] -public class AzureSearchDialog : SearchDialog -{ - private static readonly string[] TopRefiners = { "refiner1", "refiner2", "refiner3" }; // define your own custom refiners - - public AzureSearchDialog(ISearchClient searchClient) : base(searchClient, multipleSelection: true) - { - } - - protected override string[] GetTopRefiners() - { - return TopRefiners; - } -} -``` - -## Define the response data model - -The **SearchHit.cs** class within the `Search.Contracts` project defines the relevant data to be parsed from the Azure Search response. -For your bot the only mandatory inclusions are the `PropertyBag` IDictionary declaration and creation in the constructor. You can -define all other properties in this class relative to your bot's needs. - -```cs -[Serializable] -public class SearchHit -{ - public SearchHit() - { - this.PropertyBag = new Dictionary(); - } - - public IDictionary PropertyBag { get; set; } - - // customize the fields below as needed - public string Key { get; set; } - - public string Title { get; set; } - - public string PictureUrl { get; set; } - - public string Description { get; set; } -} -``` - -## After Azure Search responds - -Upon a successful query to the Azure Service, the search result will need to be parsed to retrieve the relevant data for the bot to display to -the user. To enable this, you'll need to create a `SearchResultMapper` class. The `GenericSearchResult` object created in the constructor -defines a list and dictionary to store results and facets respectively after each result is parsed respective to your bot's data models. - -Synchronize the properties in the `ToSearchHit` method to match the data model in **SearchHit.cs**. The `ToSearchHit` method will be executed -and generate a new `SearchHit` for every result found in the response. - -```cs -public class SearchResultMapper : IMapper -{ - public GenericSearchResult Map(DocumentSearchResult documentSearchResult) - { - var searchResult = new GenericSearchResult(); - - searchResult.Results = documentSearchResult.Results.Select(r => ToSearchHit(r)).ToList(); - searchResult.Facets = documentSearchResult.Facets?.ToDictionary(kv => kv.Key, kv => kv.Value.Select(f => ToFacet(f))); - - return searchResult; - } - - private static GenericFacet ToFacet(FacetResult facetResult) - { - return new GenericFacet - { - Value = facetResult.Value, - Count = facetResult.Count.Value - }; - } - - private static SearchHit ToSearchHit(SearchResult hit) - { - return new SearchHit - { - // custom properties defined in SearchHit.cs - Key = (string)hit.Document["id"], - Title = (string)hit.Document["title"], - PictureUrl = (string)hit.Document["thumbnail"], - Description = (string)hit.Document["description"] - }; - } -} -``` -After the results are parsed and stored, the information still needs to be displayed to the user. -The `SearchHitStyler` class will need to be managed to accommodate the your data model from the `SearchHit` class. For example, the `Title`, `PictureUrl`, and `Description` properties from the **SearchHit.cs** class are used in the sample code to create a new card attachments. The following code creates a new card attachment for every `SearchHit` object in the `GenericSearchResult` Results list to display to the user. - -```cs -[Serializable] -public class SearchHitStyler : PromptStyler -{ - public override void Apply(ref IMessageActivity message, string prompt, IReadOnlyList options, IReadOnlyList descriptions = null) - { - var hits = options as IList; - if (hits != null) - { - var cards = hits.Select(h => new ThumbnailCard - { - Title = h.Title, - Images = new[] { new CardImage(h.PictureUrl) }, - Buttons = new[] { new CardAction(ActionTypes.ImBack, "Pick this one", value: h.Key) }, - Text = h.Description - }); - - message.AttachmentLayout = AttachmentLayoutTypes.Carousel; - message.Attachments = cards.Select(c => c.ToAttachment()).ToList(); - message.Text = prompt; - } - else - { - base.Apply(ref message, prompt, options, descriptions); - } - } -} -``` -The search results are displayed to the user and you've successfully added Azure Search to your bot. - -## Samples - -For two complete samples that show how to support Azure Search with bots using the Bot Framework SDK for .NET, see the -[Real Estate bot sample](https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples/CSharp/demo-Search/RealEstateBot) or [Job Listing bot sample](https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples/CSharp/demo-Search/JobListingBot) in GitHub. - -## Additional resources - -- [Azure Search][search] -- [Dialogs overview](bot-builder-dotnet-dialogs.md) - -[search]: /azure/search/search-what-is-azure-search diff --git a/articles/dotnet/bot-builder-dotnet-security.md b/articles/dotnet/bot-builder-dotnet-security.md deleted file mode 100644 index a2cf2efee..000000000 --- a/articles/dotnet/bot-builder-dotnet-security.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Secure your bot - Bot Service -description: Learn how to secure your bot by using HTTPS and Bot Framework Authentication. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Secure your bot - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Your bot can be connected to many different communication channels (Skype, SMS, email, and others) through the Bot Framework Connector service. This article describes how to secure your bot by using HTTPS and Bot Framework authentication. - -## Use HTTPS and Bot Framework authentication - -To ensure that your bot's endpoint can only be accessed by the Bot Framework [Connector](bot-builder-dotnet-concepts.md#connector), configure your bot's endpoint to use only HTTPS and enable Bot Framework authentication by [registering](~/bot-service-quickstart-registration.md) your bot to acquire its appID and password. - -## Configure authentication for your bot - -Specify the bot's appID and password in your bot's web.config file. - -> [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -```xml - - - - -``` - -Then, use the `[BotAuthentication]` attribute to specify authentication credentials when using the Bot Framework SDK for .NET to create your bot. - -To use the authentication credentials that are stored in the web.config file, specify the `[BotAuthentication]` with no parameters. - -[!code-csharp[Use botAuthentication attribute](../includes/code/dotnet-security.cs#attribute1)] - -To use other values for authentication credentials, specify the `[BotAuthentication]` attribute and pass in those values. - -[!code-csharp[Use botAuthentication attribute with parameters](../includes/code/dotnet-security.cs#attribute2)] - -## Additional resources - -- [Bot Framework SDK for .NET](bot-builder-dotnet-overview.md) -- [Key concepts in the bot Builder SDK for .NET](bot-builder-dotnet-concepts.md) -- [Register a bot with the Bot Framework](~/bot-service-quickstart-registration.md) diff --git a/articles/dotnet/bot-builder-dotnet-state-azure-cosmosdb.md b/articles/dotnet/bot-builder-dotnet-state-azure-cosmosdb.md deleted file mode 100644 index 83f04ca4f..000000000 --- a/articles/dotnet/bot-builder-dotnet-state-azure-cosmosdb.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: Manage custom state data with Azure Cosmos DB (v3 C#) - Bot Service -description: Learn how to save and retrieve state data using Azure Cosmos DB with the Bot Framework SDK for .NET -author: kamrani -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage custom state data with Azure Cosmos DB for .NET - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -In this article, you’ll implement Azure Cosmos DB to store and manage your bot’s state data. The default Connector State Service used by bots is not intended for the production environment. You should either use [Azure Extensions](https://github.com/Microsoft/BotBuilder-Azure) available on GitHub or implement a custom state client using data storage platform of your choice. Here are some of the reasons to use custom state storage: - - Higher state API throughput (more control over performance) - - Lower-latency for geo-distribution - - Control over where the data is stored - - Access to the actual state data - - Store more than 32kb of data - -## Prerequisites -You'll need: - - [Microsoft Azure Account](https://azure.microsoft.com/free/) - - [Visual Studio 2015 or later](https://www.visualstudio.com/) - - [Bot Builder Azure NuGet Package](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) - - [Autofac Web Api2 NuGet Package](https://www.nuget.org/packages/Autofac.WebApi2/) - - [Bot Framework Emulator](~/bot-service-debug-emulator.md) - -## Create Azure account -If you don't have an Azure account, click [here](https://azure.microsoft.com/free/) to sign up for a free account. - -## Set up the Azure Cosmos DB database -1. After you’ve logged into the Azure portal, create a new *Azure Cosmos DB* database by clicking **New**. -2. Click **Databases**. -3. Find **Azure Cosmos DB** and click **Create**. -4. Fill in the fields. For the **API** field, select **SQL (DocumentDB)**. When done filling in all the fields, click the **Create** button at the bottom of the screen to deploy the new database. -5. After the new database is deployed, navigate to your new database. Click **Access keys** to find keys and connection strings. Your bot will use this information to call the storage service to save state data. - -## Install NuGet packages -1. Open an existing C# bot project, or create a new one using the Bot template in Visual Studio. -2. Install the following NuGet packages: - - Microsoft.Bot.Builder.Azure - - Autofac.WebApi2 - -## Add connection string -Add the following entries into the Web.config file: -```XML - - -``` -You'll replace the value with your URI and Primary Key found in your Azure Cosmos DB. Save the Web.config file. - -## Modify your bot code -To use **Azure Cosmos DB** storage, add the following lines of code to your bot's **Global.asax.cs** file inside the **Application_Start()** method. - -```cs -using System; -using Autofac; -using System.Web.Http; -using System.Configuration; -using Microsoft.Bot.Connector; -using Microsoft.Bot.Builder.Azure; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Internals; - -namespace SampleApp -{ - public class WebApiApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - var uri = new Uri(ConfigurationManager.AppSettings["DocumentDbUrl"]); - var key = ConfigurationManager.AppSettings["DocumentDbKey"]; - var store = new DocumentDbBotDataStore(uri, key); - - Conversation.UpdateContainer( - builder => - { - builder.Register(c => store) - .Keyed>(AzureModule.Key_DataStore) - .AsSelf() - .SingleInstance(); - - builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)) - .As>() - .AsSelf() - .InstancePerLifetimeScope(); - - }); - - } - } -} -``` - -Save the global.asax.cs file. Now you are ready to test the bot with the emulator. - -## Run your bot app -Run your bot in Visual Studio, the code you added will create the custom **botdata** table in Azure. - -## Connect your bot to the emulator -At this point, your bot is running locally. Next, start the emulator and then connect to your bot in the emulator: -1. Type http://localhost:port-number/api/messages into the address bar, where port-number matches the port number shown in the browser where your application is running. You can leave Microsoft App ID and Microsoft App Password fields blank for now. You'll get this information later when you [register your bot](~/bot-service-quickstart-registration.md). -2. Click **Connect**. -3. Test your bot by typing a few messages in the emulator. - -## View state data on Azure Portal -To view the state data, sign into your Azure portal and navigate to your database. Click **Data Explorer (preview)** to verify that the state information from your bot is being saved. - -## Next steps -In this article, you used Cosmos DB for saving and managing your bot's data. Next, learn how to model conversation flow by using dialogs. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-dotnet-manage-conversation-flow.md) - -## Additional resources -If you are unfamiliar with Inversion of Control containers and Dependency Injection pattern used in the code above, visit [Autofac](http://autofac.readthedocs.io/en/latest/) site for information. - -You can also download a [sample](https://github.com/Microsoft/BotBuilder-Azure/tree/master/CSharp/Samples/DocumentDb) from GitHub to learn more about using Cosmos DB for managing state. diff --git a/articles/dotnet/bot-builder-dotnet-state-azure-table-storage.md b/articles/dotnet/bot-builder-dotnet-state-azure-table-storage.md deleted file mode 100644 index ca9bc61bd..000000000 --- a/articles/dotnet/bot-builder-dotnet-state-azure-table-storage.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -title: Manage custom state data with Azure Table storage (v3 C#) - Bot Service -description: Learn how to save and retrieve state data using Azure Table Storage with the Bot Framework SDK for .NET -author: kamrani -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage custom state data with Azure Table Storage for .NET - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -In this article, you’ll implement Azure Table storage to store and manage your bot’s state data. The default Connector State Service used by bots is not intended for the production environment. You should either use [Azure Extensions](https://github.com/Microsoft/BotBuilder-Azure) available on GitHub or implement a custom state client using data storage platform of your choice. Here are some of the reasons to use custom state storage: - - Higher state API throughput (more control over performance) - - Lower-latency for geo-distribution - - Control over where the data is stored - - Access to the actual state data - - Store more than 32kb of data - -## Prerequisites -You'll need: - - [Microsoft Azure Account](https://azure.microsoft.com/free/) - - [Visual Studio 2015 or later](https://www.visualstudio.com/) - - [Bot Builder Azure NuGet Package](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) - - [Autofac Web Api2 NuGet Package](https://www.nuget.org/packages/Autofac.WebApi2/) - - [Bot Framework Emulator](https://emulator.botframework.com/) - - [Azure Storage Explorer](http://storageexplorer.com/) - -## Create Azure account -If you don't have an Azure account, click [here](https://azure.microsoft.com/free/) to sign up for a free account. - -## Set up the Azure Table storage service -1. After you’ve logged into the Azure portal, create a new Azure Table storage service by clicking on **New**. -2. Search for **Storage account** that implements the Azure Table. -3. Fill in the fields, click the **Create** button at the bottom of the screen to deploy the new storage service. After the new storage service is deployed, it will display features and options available to you. -4. Select the **Access keys** tab on the left, and copy the connection string for later use. Your bot will use this connection string to call the storage service to save state data. - -## Install NuGet packages -1. Open an existing C# bot project, or create a new one using the C# Bot template in Visual Studio. -2. Install the following NuGet packages: - - Microsoft.Bot.Builder.Azure - - Autofac.WebApi2 - -## Add connection string -Add the following entry in your Web.config file: -```XML - - - -``` -Replace "YourConnectionString" with the connection string to the Azure Table storage you saved earlier. Save the Web.config file. - -## Modify your bot code -In the Global.asax.cs file, add the following `using` statements: -```cs -using Autofac; -using System.Configuration; -using Microsoft.Bot.Connector; -using Microsoft.Bot.Builder.Azure; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Internals; -``` -In the `Application_Start()` method, create an instance of the `TableBotDataStore` class. The `TableBotDataStore` class implements the `IBotDataStore` interface. The `IBotDataStore` interface allows you to override the default Connector State Service connection. - ```cs - var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); - ``` -Register the service as shown below: - ```cs - Conversation.UpdateContainer( - builder => - { - builder.Register(c => store) - .Keyed>(AzureModule.Key_DataStore) - .AsSelf() - .SingleInstance(); - - builder.Register(c => new CachingBotDataStore(store, - CachingBotDataStoreConsistencyPolicy - .ETagBasedConsistency)) - .As>() - .AsSelf() - .InstancePerLifetimeScope(); - - - }); - ``` -Save the Global.asax.cs file. - -## Run your bot app -Run your bot in Visual Studio, the code you added will create the custom **botdata** table in Azure. - -## Connect your bot to the emulator -At this point, your bot is running locally. Next, start the emulator and then connect to your bot in the emulator: -1. Type http://localhost:port-number/api/messages into the address bar, where port-number matches the port number shown in the browser where your application is running. You can leave Microsoft App ID and Microsoft App Password fields blank for now. You'll get this information later when you [register your bot](~/bot-service-quickstart-registration.md). -2. Click **Connect**. -3. Test your bot by typing a few messages in the emulator. - -## View data in Azure Table storage -To view the state data, open **Storage Explorer** and connect to Azure using your Azure Portal credential or connect directly to the table using the storage name and storage key then navigate to your table name. - -## Next steps -In this article, you implemented Azure Table storage for saving and managing your bot's data. Next, learn how to model conversation flow by using dialogs. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-dotnet-manage-conversation-flow.md) - - -## Additional resources - -If you are unfamiliar with Inversion of Control containers and Dependency Injection pattern used in the code above, go to [Autofac](http://autofac.readthedocs.io/en/latest/) site for information. - -You can also download a complete [Azure Table storage](https://github.com/Microsoft/BotBuilder-Azure/tree/master/CSharp/Samples/AzureTable) sample from GitHub. diff --git a/articles/dotnet/bot-builder-dotnet-state.md b/articles/dotnet/bot-builder-dotnet-state.md deleted file mode 100644 index 83bb1b565..000000000 --- a/articles/dotnet/bot-builder-dotnet-state.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: Manage state data (v3 C#) - Bot Service -description: Learn how to save and retrieve state data with the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage state data - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-state.md) -> - [Node.js](../nodejs/bot-builder-nodejs-state.md) - -[!INCLUDE [State concept overview](../includes/snippet-dotnet-concept-state.md)] - -## In-memory data storage - -In-memory data storage is intended for testing only. This storage is volatile and temporary. The data is cleared each time the bot is restarted. To use the in-memory storage for testing purposes, you will need to: - -Install the following NuGet packages: -- Microsoft.Bot.Builder.Azure -- Autofac.WebApi2 - -In the **Application_Start** method, create a new instance of the in-memory storage, and register the new data store: - -```cs -// Global.asax file - -var store = new InMemoryDataStore(); - -Conversation.UpdateContainer( - builder => - { - builder.Register(c => store) - .Keyed>(AzureModule.Key_DataStore) - .AsSelf() - .SingleInstance(); - - builder.Register(c => new CachingBotDataStore(store, - CachingBotDataStoreConsistencyPolicy - .ETagBasedConsistency)) - .As>() - .AsSelf() - .InstancePerLifetimeScope(); - - - }); -GlobalConfiguration.Configure(WebApiConfig.Register); - -``` - -You can use this method to set your own custom data storage or use any of the *Azure Extensions*. - -## Manage custom data storage - -For performance and security reasons in the production environment, you may implement your own data storage or consider implementing one of the following data storage options: - -1. [Manage state data with Cosmos DB](bot-builder-dotnet-state-azure-cosmosdb.md) - -2. [Manage state data with Table storage](bot-builder-dotnet-state-azure-table-storage.md) - -With either of these [Azure Extensions](https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/) options, the mechanism for setting and persisting data via the Bot Framework SDK for .NET remains the same as the in-memory data storage. - -## Bot state methods - -This table lists the methods that you can use to manage state data. - -| Method | Scoped to | Objective | -|----|----|----| -| `GetUserData` | User | Get state data that has previously been saved for the user on the specified channel | -| `GetConversationData` | Conversation | Get state data that has previously been saved for the conversation on the specified channel | -| `GetPrivateConversationData` | User and Conversation | Get state data that has previously been saved for the user within the conversation on the specified channel | -| `SetUserData` | User | Save state data for the user on the specified channel | -| `SetConversationData` | Conversation | Save state data for the conversation on the specified channel.

**Note**: Because the `DeleteStateForUser` method does not delete data that has been stored using the `SetConversationData` method, you must NOT use this method to store a user's personally identifiable information (PII). | -| `SetPrivateConversationData` | User and Conversation | Save state data for the user within the conversation on the specified channel | -| `DeleteStateForUser` | User | Delete state data for the user that has previously been stored by using either the `SetUserData` method or the `SetPrivateConversationData` method.

**Note**: Your bot should call this method when it receives an activity of type [deleteUserData](bot-builder-dotnet-activities.md#deleteuserdata) or an activity of type [contactRelationUpdate](bot-builder-dotnet-activities.md#contactrelationupdate) that indicates the bot has been removed from the user's contact list. | - -If your bot saves state data by using one of the "**Set...Data**" methods, future messages that your bot receives in the same context will contain that data, which your bot can access by using the corresponding "**Get...Data**" method. - -## Useful properties for managing state data - -Each [Activity][Activity] object contains properties that you will use to manage state data. - -| Property | Description | Use case | -|----|----|----| -| `From` | Uniquely identifies a user on a channel | Storing and retrieving state data that is associated with a user | -| `Conversation` | Uniquely identifies a conversation | Storing and retrieving state data that is associated with a conversation | -| `From` and `Conversation` | Uniquely identifies a user and conversation | Storing and retrieving state data that is associated with a specific user within the context of a specific conversation | - -> [!NOTE] -> You may use these property values as keys even if you opt to store state data in your own database, rather than using the Bot Framework state data store. - -## Handle concurrency issues - -Your bot may receive an error response with HTTP status code **412 Precondition Failed** -when it attempts to save state data, if another instance of the bot has changed the data. -You can design your bot to account for this scenario, as shown in the following code example. - -[!code-csharp[Handle exception saving state](../includes/code/dotnet-state.cs#handleException)] - -## Additional resources - -- [Bot Framework troubleshooting guide](../bot-service-troubleshoot-general-problems.md) -- Bot Framework SDK for .NET Reference - -[Activity]: https://docs.botframework.com/csharp/builder/sdkreference/dc/d2f/class_microsoft_1_1_bot_1_1_connector_1_1_activity.html diff --git a/articles/dotnet/bot-builder-dotnet-text-to-speech.md b/articles/dotnet/bot-builder-dotnet-text-to-speech.md deleted file mode 100644 index f173d6175..000000000 --- a/articles/dotnet/bot-builder-dotnet-text-to-speech.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Add speech to messages (v3 C#) - Bot Service -description: Learn how to add speech to messages using the Bot Framework SDK for .NET. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add speech to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-text-to-speech.md) -> - [Node.js](../nodejs/bot-builder-nodejs-text-to-speech.md) -> - [REST](../rest-api/bot-framework-rest-connector-text-to-speech.md) - -If you are building a bot for a speech-enabled channel such as Cortana, you can construct messages that specify the text to be spoken by your bot. You can also attempt to influence the state of the client's microphone by specifying an [input hint](bot-builder-dotnet-add-input-hints.md) to indicate whether your bot is accepting, expecting, or ignoring user input. - -## Specify text to be spoken by your bot - -Using the Bot Framework SDK for .NET, there are multiple ways to specify the text to be spoken by your bot on a speech-enabled channel. You can set the `Speak` property of the [message][IMessageActivity], call the `IDialogContext.SayAsync()` method, or specify prompt options `speak` and `retrySpeak` when sending a message using a built-in prompt. - -### IMessageActivity.Speak - -If you are creating a [message][IMessageActivity] and setting its individual properties, you can set the `Speak` property of the message to specify the text to be spoken by your bot. The following code example creates a message that specifies text to be displayed and text to be spoken and indicates that the bot is [accepting user input](bot-builder-dotnet-add-input-hints.md). - -[!code-csharp[Set speak property](../includes/code/dotnet-text-to-speech.cs#Speak1)] - -### IDialogContext.SayAsync() - -If you are using [dialogs](bot-builder-dotnet-dialogs.md), you can call the `SayAsync()` method to create and send a message that specifies the text to be spoken, in addition to the text to be displayed and other options. The following code example creates a message that specifies text to be displayed and text to be spoken. - -[!code-csharp[Call SayAsync()](../includes/code/dotnet-text-to-speech.cs#Speak2)] - -### Prompt options - -Using any of the built-in prompts, you can set the options `speak` and `retrySpeak` to specify the text to be spoken by your bot. The following code example creates a prompt that specifies text to be displayed, text to be spoken initially, and text to be spoken after waiting a while for user input. It uses [SSML](#ssml) formatting to indicate that the word "sure" should be spoken with a moderate amount of emphasis. - -[!code-csharp[Set Prompt options](../includes/code/dotnet-text-to-speech.cs#Speak3)] - -## Speech Synthesis Markup Language (SSML) - -To specify text to be spoken by your bot, you can give it a string that is formatted as Speech Synthesis Markup Language (SSML). SSML is an XML-based markup language (and therefore must be valid XML) that enables you to control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, pitch, and more. For details about SSML, see Speech Synthesis Markup Language Reference. - -When providing the SSML formatted string, the outer SSML wrapper element may be omitted. - -## Input hints - -When you send a message on a speech-enabled channel, you can attempt to influence the state of the client's microphone by also including an input hint to indicate whether your bot is accepting, expecting, or ignoring user input. For more information, see [Add input hints to messages](bot-builder-dotnet-add-input-hints.md). - -## Sample code - -For a complete sample that shows how to create a speech-enabled bot using the Bot Framework SDK for .NET, see the Roller Skill sample in GitHub. - -## Additional resources - -- [Create messages](bot-builder-dotnet-create-messages.md) -- [Add input hints to messages](bot-builder-dotnet-add-input-hints.md) -- Speech Synthesis Markup Language (SSML) -- Roller Skill sample (GitHub) -- Activity class -- IMessageActivity interface -- DialogContext class -- Prompt class - -[IMessageActivity]: /dotnet/api/microsoft.bot.connector.imessageactivity - diff --git a/articles/file-format/bot-builder-lg-file-format.md b/articles/file-format/bot-builder-lg-file-format.md new file mode 100644 index 000000000..f578fc9d8 --- /dev/null +++ b/articles/file-format/bot-builder-lg-file-format.md @@ -0,0 +1,575 @@ +--- +title: .lg file format - Bot Service +description: .lg file format reference +keywords: lg file format, reference, language generation +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# .lg file format + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +The .lg file describes language generation templates with entity references and their composition. This article covers the various concepts expressed with the .lg file format. + +## Special Characters + +### Comments + +Use **>** to create a comment. All lines that have this prefix will be skipped by the parser. + +```lg +> This is a comment. +``` + +### Escape character + +Use **\\** as an escape character. + +```lg +# TemplateName +- You can say cheese and tomato \[toppings are optional\] +``` + +## Arrays and objects + +### Create an array + +To create an array, use the **${[object1, object2, ...]}** syntax. For example, this expression: + +```lg +${['a', 'b', 'c']} +``` + +Returns the array `['a', 'b', 'c']`. + +### Create an object + +To create an object, use the **${{key1:value1, key2:value2, ...}}** syntax. For example, this expression: + +```lg +${{user: {name: "Wilson", age: 27}}} +``` + +Returns the following JSON object: + +```json +{ + "user": { + "name": "Wilson", + "age": 27 + } +} +``` + +## Templates + +**Templates** are the core concept of the language generation system. Each template has a name and one of the following: + +- a list of one-of variation text values +- a structured content definition +- a collection of conditions, each with: + - an [adaptive expression][3] + - a list of one-of variation text values per condition + +### Template names + +Template names are case-sensitive and can only contain letters, underscores, and numbers. The following is an example of a template named `TemplateName`. + +```lg +# TemplateName +``` + +Templates can't start with a number, and any part of a template name split by **.** can't start with a number. + +### Template response variations + +Variations are expressed as a Markdown list. You can prefix each variation using the **-**, **'**, or **+** character. + +```lg +# Template1 +- text variation 1 +- text variation 2 +- one +- two + +# Template2 +* text variation 1 +* text variation 2 + +# Template3 ++ one ++ two +``` + +### Simple response template + +A simple response template includes one or more variations of text that are used for composition and expansion. One of the variations provided will be selected at random by the LG library. + +Here is an example of a simple template that includes two variations. + +```lg +> Greeting template with two variations. +# GreetingPrefix +- Hi +- Hello +``` + +### Conditional response template + +Conditional response templates let you author content that's selected based on a condition. All conditions are expressed using [adaptive expressions][3]. + +> [!IMPORTANT] +> Conditional templates can't be nested in a single conditional response template. Use composition in a [structured response template](../language-generation/language-generation-structured-response-template.md) to nest conditionals. + +#### If-else template + +The if-else template lets you build a template that picks a collection based on a cascading order of conditions. Evaluation is top-down and stops when a condition evaluates to `true` or the ELSE block is hit. + +Conditional expressions are enclosed in braces **${}**. Here's an example that shows a simple IF ELSE conditional response template definition. + +```lg +> time of day greeting reply template with conditions. +# timeOfDayGreeting +- IF: ${timeOfDay == 'morning'} + - good morning +- ELSE: + - good evening +``` + +Here's another example that shows an if-else conditional response template definition. Note that you can include references to other simple or conditional response templates in the variation for any of the conditions. + +```lg +# timeOfDayGreeting +- IF: ${timeOfDay == 'morning'} + - ${morningTemplate()} +- ELSEIF: ${timeOfDay == 'afternoon'} + - ${afternoonTemplate()} +- ELSE: + - I love the evenings! Just saying. ${eveningTemplate()} +``` + +#### Switch template + +The switch template lets you design a template that matches an expression's value to a CASE clause and produces output based on that case. Condition expressions are enclosed in braces **${}**. + +Here's how you can specify a SWITCH CASE DEFAULT block in LG. + +```lg +# TestTemplate +- SWITCH: ${condition} +- CASE: ${case-expression-1} + - output1 +- CASE: ${case-expression-2} + - output2 +- DEFAULT: + - final output +``` + +Here's a more complicated SWITCH CASE DEFAULT example: + +```lg +> Note: Any of the cases can include reference to one or more templates. +# greetInAWeek +- SWITCH: ${dayOfWeek(utcNow())} +- CASE: ${0} + - Happy Sunday! +-CASE: ${6} + - Happy Saturday! +-DEFAULT: + - ${apology-phrase()}, ${defaultResponseTemplate()} +``` + +> [!NOTE] +> Like conditional templates, switch templates can't be nested. + +### Structured response template + +Structured response templates let you define a complex structure that supports major LG functionality, like templating, composition, and substitution, while leaving the interpretation of the structured response up to the caller of the LG library. + +For bot applications, we natively support: + +- activity definition +- card definition + +Read about [structure response templates](../language-generation/language-generation-structured-response-template.md) for more information. + +## Template composition and expansion + +### References to templates + +Variation text can include references to another named template to aid with composition and resolution of sophisticated responses. References to other named templates are denoted using braces, such as **${\()}**. + +```lg +> Example of a template that includes composition reference to another template. +# GreetingReply +- ${GreetingPrefix()}, ${timeOfDayGreeting()} + +# GreetingPrefix +- Hi +- Hello + +# timeOfDayGreeting +- IF: ${timeOfDay == 'morning'} + - good morning +- ELSEIF: ${timeOfDay == 'afternoon'} + - good afternoon +- ELSE: + - good evening +``` + +Calling the `GreetingReply` template can result in one of the following expansion resolutions: + +```lg +Hi, good morning +Hi, good afternoon +Hi, good evening +Hello, good morning +Hello, good afternoon +Hello, good evening +``` + +## Entities + +When used directly within a one-of variation text, entity references are denoted by enclosing them in braces, such as ${`entityName`}, or without braces when used as a parameter. + +Entities can be used as a parameter: + +- within a [prebuilt function][4] +- within a condition in a [conditional response template](#conditional-response-template) +- to [template resolution call](#parametrization-of-templates) + +## Using prebuilt functions in variations + +[Prebuilt functions][4] supported by [adaptive expressions][3] can also be used inline in a one-of variation text to achieve even more powerful text composition. To use an expression inline, simply wrap it in braces. + +```lg +# RecentTasks +- IF: ${count(recentTasks) == 1} + - Your most recent task is ${recentTasks[0]}. You can let me know if you want to add or complete a task. +- ELSEIF: ${count(recentTasks) == 2} + - Your most recent tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task. +- ELSEIF: ${count(recentTasks) > 2} + - Your most recent ${count(recentTasks)} tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task. +- ELSE: + - You don't have any tasks. +``` + +The example above uses the [join][5] prebuilt function to list all values in the `recentTasks` collection. + +Given templates and prebuilt functions share the same invocation signature, a template name can't be the same as a prebuilt function name. + + A template name shouldn't match a prebuilt function name. The prebuilt function takes precedence. To avoid such conflicts, you can prepend `lg.` when referencing your template name. For example: + +```lg +> Custom length function with one parameter. +# length(a) +- This is use's customized length function + +# myfunc1 +> will call prebuilt function length, and return 2 +- ${length('hi')} + +# mufunc2 +> this calls the lg template and output 'This is use's customized length function' +- ${lg.length('hi')} +``` + +## Multiline text in variations + +Each one-of variation can include multiline text enclosed in triple quotes. + +```lg +# MultiLineExample + - ```This is a multiline list + - one + - two + ``` + - ```This is a multiline variation + - three + - four + ``` +``` + +Multiline variation can request template expansion and entity substitution by enclosing the requested operation in braces, ${}. + +```lg +# MultiLineExample + - ``` + Here is what I have for the order + - Title: ${reservation.title} + - Location: ${reservation.location} + - Date/ time: ${reservation.dateTimeReadBack} + ``` +``` + +With multiline support, you can have the Language Generation sub-system fully resolve a complex JSON or XML (like SSML wrapped text to control bot's spoken reply). + +## Parametrization of templates + +To aid with contextual reusability, templates can be parametrized. Different callers to the template can pass in different values for use in expansion resolution. + +```lg +# timeOfDayGreetingTemplate (param1) +- IF: ${param1 == 'morning'} + - good morning +- ELSEIF: ${param1 == 'afternoon'} + - good afternoon +- ELSE: + - good evening + +# morningGreeting +- ${timeOfDayGreetingTemplate('morning')} + +# timeOfDayGreeting +- ${timeOfDayGreetingTemplate(timeOfDay)} +``` + +## Importing external references + +You can split your language generation templates into separate files and reference a template from one file in another. You can use Markdown-style links to import templates defined in another file. + +```lg +[Link description](filePathOrUri) +``` + +All templates defined in the target file will be pulled in. Ensure that your template names are unique (or namespaced with `# \.\`) across files being pulled in. + +```lg +[Shared](../shared/common.lg) +``` + +## Functions injected by LG + +[Adaptive expressions][3] provide the ability to inject a custom set of functions. Read [functions injected from the LG library][13] for more information. + +## Options + +Developers can set parser options to further customize how input is evaluated. Use the `> !#` notation to set parser options. + +> [!IMPORTANT] +> +> The last setting found in the file trumps any prior setting found in the same document. + +### Strict option + +Developers who don't want to allow a null result for a null evaluated result can implement the **strict** option. Below is an example of a simple strict option: + +```lg +> !# @strict = true +# template +- hi +``` + +If the strict option is on, null errors will throw a friendly message. + +```lg +# welcome +- hi ${name} +``` + +If name is null, the diagnostic would be **'name' evaluated to null. [welcome] Error occurred when evaluating '- hi ${name}'.**. If strict is set to false or not set, a compatible result will be given. The above sample would produce **hi null**. + +### replaceNull option + +Developers can create delegates to replace null values in evaluated expressions by using the **replaceNull** option: + +```lg +> !# @replaceNull = ${path} is undefined +``` + +In the above example, the null input in the `path` variable would be replaced with **${path} is undefined**. The following input, where `user.name` is null: +: + +```lg +hi ${user.name} +``` + +Would result in **hi user.name is undefined**. + +### lineBreakStyle option + +Developers can set options for how the LG system renders line breaks using the **lineBreakStyle** option. Two modes are currently supported: + +- `default`: line breaks in multiline text create normal line breaks. +- `markdown`: line breaks in multiline text will be automatically converted to two lines to create a newline + +The example below shows how to set the lineBreakStyle option to `markdown`: + +```lg +> !# @lineBreakStyle = markdown +``` + +### Namespace option + +You can register a namespace for the LG templates you want to export. If there is no namespace specified, the namespace will be set to the filename without an extension. + +The example below shows how to set the namespace option to `foo`: + +```lg +> !# @Namespace = foo +``` + +### Exports option + +You can specify a list of LG templates to export. The exported templates can be called like prebuilt functions. + +The example below shows how to set the exports option to `template1, template2`: + +```lg +> !# @Namespace = foo +> !# @Exports = template1, template2 + +# template1(a, b) +- ${a + b} + +# template2(a, b) +- ${join(a, b)} +``` + +Use `foo.template1(1,2), foo.template2(['a', 'b', 'c'], ',')` to call these exported templates. + +### Cache scope + +The _cache scope_ options let you control when the LG evaluator reevaluates an expression it has seen before and when it stores and uses a cached result. + +- _Global cache_ is effective in the life cycle of an evaluation. LG caches all evaluation results, and if the template name and parameters are the same, returns the result from the cache. +- _Local cache_ scope is the default. In the same layer, if the previous template has been called with the same template name and the same parameters, the cached result is directly returned. +- _None cache_ scope disables all cache scope, and each time returns the new result. + +For examples, see the [global](#global-cache-scope-example) and [local](#local-cache-scope-example) cache scope examples. + +```lg +> !# @cacheScope= global // global cache +> !# @cacheScope= local // local cache +> !# @cacheScope= none // none cache +> !# @cacheScope= xxx // fallback to local cache +``` + +Note that the cache scope option is case-insensitive. + +```lg +> !# @cacheScope= global // ok +> !# @CACHESCOPE= global // ok +> !# @cachescope= global // ok +``` + +Note that cache scope follows the scope of the Microsoft Entrance .lg file. + +Say you have two files: `a.lg` and `b.lg`, shown below: + +**a.lg** + +```lg +> !# @cacheScope= global + [import](b.lg) +``` + +**b.lg** + +```lg +> !# @cacheScope= none +# template1 +- ${template2()} ${template2()} + +# template2 +- ${rand(1, 10000000)} +``` + +If you run the following code, you'll notice that `template2` uses the cached result of the first evaluated result because of the `global` cache scope option in **a.lg**: + +```csharp +var templates = Templates.ParseFile("a.lg"); +var result = templates.Evaluate("template1"); // the second "template2" would use the cache of the first evaluate result +``` + +#### Re-execute mark influence + +If the template name ends with "!", the template forces re-execution. This result won't be added to the cache regardless of the cache scope. + +Say you have following template: + +```lg +# template2 +- ${template1()} ${template1!()} ${template1()} +``` + +`template1!()` fires and the result is added to the cache. The second `template1()` clobbers the result from the first `template1()`. The final call uses the results stored in the cache. + +#### Global cache scope example + +Say you have the following templates: + +```lg +# template1 +- ${template2()} ${template3()} + +# template2 +- ${rand(1, 10)} +- abc +- hi + +# template3 +- ${template2()} +``` + +`template2` would be evaluated once, and the second execution in `template3` would apply the cache of the first one. + +Another example is in the following code snippet: + +```csharp +var templates = Templates.ParseFile("xxx.lg"); +var result1 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global}); + +// The second evaluation would drop all the results cached before. +var result2 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global}); +``` + +A template is parsed using the `Templates.ParseFile()` function, and the template evaluation results are stored in `result1`. Note that the second evaluation results, `result2`, drops all results previously cached. + +#### Local cache scope example + +The following examples show when the local cache scope does and doesn't work. Assume that `t()` and `subT()` are templates that take a parameter: + +```lg +> Cache works, the second template call would re-use the first's result. +# template1 +- ${t(param)} ${t(param)} + +> Cache doesn't work because param1's value is different with param2's. value) +# template2 +- ${t(param1)} ${t(param2)} + +> Cache doesn't work because of different layers. +# template3 +- ${subT(param1)} ${t(param2)} + +# subT(param) +- ${t(param)} +``` + +## Additional Resources + +- [C# API Reference](/dotnet/api/microsoft.bot.builder.languagegeneration) +- [JavaScript API reference](/javascript/api/botbuilder-lg) +- Read [Debug with Adaptive Tools](../bot-service-debug-adaptive-tools.md) to learn how to analyze and debug .lg files. + +[1]:https://github.com/Microsoft/botbuilder-tools/blob/master/packages/Ludown/docs/lu-file-format.md +[3]:../v4sdk/bot-builder-concept-adaptive-expressions.md +[4]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md +[5]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#join +[6]:https://github.com/microsoft/botframework-cli/tree/master/packages/chatdown +[7]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md +[8]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/examples/CardExamples.chat +[9]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-commands +[10]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-cards +[11]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-attachments +[12]:https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md +[13]:../language-generation/functions-injected-from-language-generation.md diff --git a/articles/file-format/bot-builder-lu-file-format.md b/articles/file-format/bot-builder-lu-file-format.md new file mode 100644 index 000000000..05e94ec34 --- /dev/null +++ b/articles/file-format/bot-builder-lu-file-format.md @@ -0,0 +1,765 @@ +--- +title: .lu file format +description: .lu file format reference +keywords: lu file format, reference, language understanding +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# .lu file format + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +An .lu file describes a language understanding model. +An .lu file contains Markdown-like, simple text-based definitions for language understanding concepts. +You can use one or more .lu files to train a language model for the natural language understanding (NLU) service or engine that your bot uses, such as [Language Understanding (LUIS)][LUIS] or [Orchestrator][]. +The NLU engine you choose may only be able to interpret subset of the elements that an .lu file can describe. + +An NLU engine relies on a language model to understand what a user says. The engine creates a language model from sets of training examples, just like any machine learning algorithm. Once trained, the engine uses the model to predict the intent of an _utterance_, generally in the form of one or more _intents_ that represent a task or action the user wants to perform and zero or more _entities_ that represent elements relevant to the intent. + +You can use LUIS or Orchestrator with any bot developed using the Bot Framework SDK or [Composer][]. + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + +This article is a reference for how to represent language model elements in the .lu file format. For information about how language understanding is used in bots, see [Language Understanding](../v4sdk/bot-builder-concept-luis.md) or [Natural language processing in Composer](/composer/concept-natural-language-processing). + +## Defining intents using sample utterances + +An intent represents a task or action the user wants to perform, as expressed in a user's utterance. You add intents to your bot to enable it to identify groups of questions or commands that represent the same user intention. + +Some examples of intents you might define for a travel bot, with the example utterances that they're defined from: + +| Intent | Example utterances | +|--------------|------------------------------------------------------------------------------------------------------------------------------| +| BookFlight | "Book me a flight to Maui next week"
"Fly me to Maui on the 17th"
"I need a plane ticket next Friday to Maui" | +| Greeting | "Hi"
"Hello"
"Good afternoon" | +| CheckWeather | "What's the weather like in Maui next week?" | +| None | "I like cookies"
"Bullfrogs have been recorded jumping over 7 feet" | + +In addition to the intents that you define, **None** is a fallback intent that causes the `unknownIntent` event to fire when no intents can be determined from the users utterance. When using LUIS, the **None** intent is a required intent that you need to create with utterances that are outside of your domain. The utterances associated with your **None** intent should comprise roughly 10% of the total utterances in your .lu file. + +Intents with their sample utterances are declared in the following way: + +```lu +# + - + - +``` + +`# ` describes a new intent definition section. Each line after the intent definition are example utterances that describe that intent using the `- ` format. + +Here is an example .lu file demonstrating these intents and example utterances that capture ways users can express the intent: + +```lu +> Use ">" to create a comment in your .lu files. +> Use multiple comment ">" characters to define outlining +> sections in the file to help you organize the content. + +>> Primary intents +# BookFlight +- Book me a flight to Maui next week +- Fly me to Maui on the 17th +- I need a plane ticket next Friday to Maui + +# Greeting +- Hi +- Hello +- Good afternoon + +>> Secondary intents +# CheckWeather +- What's the weather like in Maui next week? +``` + +> [!NOTE] +> Use the **-**, **+**, or **\*** character to denote lists. Numbered lists are not supported. +> +> Use **>** to create a comment. +> +> Multiple comment ("**>**") characters can also be used to define outlining sections in the .lu file to help you organize the content. [Composer][] allows you to take advantage of outlining when editing LU files. + +For more information about intents and utterances, see [Intents in your LUIS app](/azure/ai-services/luis/concepts/intents) and [Understand what good utterances are for your LUIS app](/azure/ai-services/luis/concepts/utterances) in the LUIS documentation. + +## Entities + +An entity is part of an utterance that can be thought of as a parameter that can be used in the interpretation of an intent. For example, in the utterance _Book a ticket to Maui_, _Maui_ is a FlightDestination +entity. + +|Sample user utterance|Intent predicted|Entities extracted|Explanation| +|---------------------|----------------|------------------|-----------| +|Hello, how are you?|Greeting| - |No entities to extract.| +|"Book a flight to Maui"|BookFlight|"Maui"|"FlightDestination" entity is extracted as "Maui".| +|"What's the weather like in Maui next week?"|CheckWeather|"Maui", "next week"|"WeatherLocation" entity is extracted as "Maui" and "DateRange" entity is extracted as "next week".| +|"I want to order a small pizza"|orderPizza|"small"|"Size" entity is extracted as "small".| +|"Schedule a meeting at 1pm with Bob in Distribution"|ScheduleMeeting|"1pm", "Bob"|"MeetingTime" entity is extracted as "1pm" and "Attendees" entity is extracted as "Bob".| + +> [!TIP] +> For more information specific to using entities in LUIS, see [Entities in LUIS][entity] in the LUIS documentation. + +### Entity definitions + +An entity definition defines how to recognize a span in an utterance as an entity that you can then use in your bot. There are many different types of entities including: machine-learned, prebuilt, lists, regular expressions, and patterns. + +Entity definitions in .lu files start the entry with the at sign (`@`) followed by the type of entity and entity name: + + ```lu +@ +``` + +Optionally, each entity can also have [roles](#roles) that identify different uses of the same entity. You can also add [features](#adding-features-to-intents-and-entities) to help do a better job of recognizing entities. The general syntax looks like this: + + ```lu +@ [[hasRole[s]] ] [hasFeature[s] ] +``` + +Entities that require a definition, like [list](#list-entity) and [regular expression](#regular-expression-entity) entities, are represented using the following notation: + +```lu +@ = +``` + +Additional examples of entity declarations will be demonstrated in the following sections along with the entity types they apply to. + +With the exception of [prebuilt entities](#prebuilt-entities), entity names can contain multiple words with spaces. All entity names with spaces must be wrapped in quotes: + + ```lu +@ ml "this is a simple entity" role1, role2 = definition +@ ml 'another simple entity' hasRole role1 hasFeatures feature1, feature2 +``` + +### Entity types + +There are several types of entities in LUIS. In the following sections, you'll learn about these entity types and related concepts, such as [roles](#roles) and [features](#adding-features-to-intents-and-entities), and examples of how to create LU templates that use them. + +#### Machine-learned entity + +[Machine-learned entities][ml-entity] are entities that enable you to provide examples where you label them in the example utterances. This gives them the context needed to learn from. The machine-learned entity is ideal when identifying data that isn't always well formatted but has the same meaning. + +The following example demonstrates a machine-learned entity named city (`@ ml city`) and a `bookFlight` intent with sample utterances with your entities labeled: + +```lu +> Define the city machine-learned entity +@ ml city + +> Define the bookFlight intent with sample utterances that contain the machine-learned entities +# bookFlight +- Book a flight from {@city = Cairo} to {@city = Seattle} +- Get me 2 tickets for a flight to {@city = Bengaluru} +- Purchase ticket from {@city = Washington} to {@city = Tampa Bay} +``` + +When a user says something similar like "_I need a flight booked from London to madrid_", LUIS will detect the 'bookFlight` intent and extract both London and Madrid as city entities. + +[Roles](#roles) are essentially an additional layer of contextual information you can add to your machine-learned entities, that also learn from context. The following example utterance shows the departure and destination roles associated with the city entity: + +```lu +- Book a flight from {@city:departure = Cairo} to {@city:destination = Seattle} +``` + +Machine-learned entities can also be complex where they have a hierarchy of entities related to each other. For example, you can have something like a `pizzaOrder` entity that has the following children entities: quantity, size, crust, toppings, and so on. + +You define a child entity by prepending a dash (-) to the at sign (@) and indenting, as the following example demonstrates: + +```lu +@ prebuilt number +@ list sizeList +@ list crustList +@ list toppingList + +@ ml pizzaOrder + - @ number Quantity + - @ sizeList Size + - @ crustList Crust + - @ toppingList Topping +``` + +In the above example, the number entity is a [prebuilt entity](#prebuilt-entities). The remaining entities are all [list entities](#list-entity). + +The next example shows a definition of an `address` machine-learned entity, with `fromAddress` and `toAddress` as two roles, as well as children. + +```lu +@ list cityList +@ prebuilt number +@ prebuilt geographyV2 +@ regex regexZipcode = /[0-9]{5}/ +@ ml address hasRoles fromAddress, toAddress +@ address = + - @ number 'door number' + - @ ml streetName + - @ ml location usesFeature geographyV2 + - @ cityList city + - @ regexZipcode zipcode +``` + +#### Prebuilt entities + +Prebuilt LUIS entities are defined by the system. This saves you work since they're of high quality and provide normalized values that are easier to use in programs. For example the phrase "one thousand and two" would become the number 1002. The following LUIS [prebuilt entity][prebuilt-entity] types are supported: + +- age +- datetimeV2 +- dimension +- email +- geographyV2 +- keyPhrase +- money +- number +- ordinal +- ordinalV2 +- percentage +- personName +- phonenumber +- temperature +- url +- datetime + +Here are examples of how to define prebuilt entities: + +```lu +@ prebuilt number +@ prebuilt datetimeV2 +@ prebuilt age +``` + +#### List entity + +[List entities][list-entity] represent a fixed, closed set of related words along with their synonyms. The normalized value is returned when any of the corresponding synonyms are recognized. They're case-sensitive and extracted based on an exact text match. + +The following example shows the syntax for defining a list entity: + +```lu +@ list = + - : + - + - + - ... + - : + - , , ... +``` + +Extending the `pizzaOrder` example from the machine-learned entity section, here is an example of lists for the size and crust child entities: + +```lu +@ list sizeList = + - Extra Large : + - extra large + - XL + - xl + - huge + - massive + - Large: + - large + - big + - Medium : + - medium + - regular + - Small : + - small + - smallest + - individual + +@ list crustList = + - Stuffed Crust : + - stuffed crust + - stufffed crust + - Thin : + - thin + - thin crust + - Thick : + - thick + - thick crust + - Deep Dish + - deep dish +``` + +> [!TIP] +> Since a list entity requires an exact match to be extracted, your results may improve by adding common misspellings. One common causes of misspellings is a result of typing errors such as double letters tripled as in "stufffed crust" in the above example. + +When using list entities you should include a value from the list directly in the utterance, you don't need to label list entities although you can still use them as place holders in a [pattern](#patterns). The following example shows an utterance with values from the list: + +```lu +- I'd like to order a large pepperoni stuffed crust pizza. +``` + +#### Regular expression entity + +A [regular expression entity][regular-expression-entity] extracts an entity based on a regular expression character pattern you provide. Regular expressions are best for structured text or a predefined sequence of alphanumeric values that are expected in a certain format. For example: + +| Entity | Regular expression | Example | +|-------------------|--------------------------|------------------| +|Flight Number | flight [A-Z]{2} [0-9]{4} | flight AS 1234 | +|Credit Card Number | [0-9]{16} | 5478789865437632 | + +Here's an example of the regular expression entity definitions: + +```lu +> Flight Number regular expression entity definition +@ regex flightNumber = /flight [A-Z]{2} [0-9]{4}/ + +> Credit Card Number regular expression entity definition +@ regex creditCardNumber = /[0-9]{16}/ +``` + +## Roles + +A role is a named alias for an entity based on context within an utterance. A role can be used with any prebuilt or custom entity type and are used in both example utterances and patterns. + +In the example below the **Location** entity has two roles, `origin` and `destination`: + +|Entity |Role |Purpose | +|---------|------------|----------------------------| +|Location |origin |Where the plane departs from| +|Location |destination |Where the plane lands | + +Roles in .lu file format can be explicitly or implicitly defined. Explicit role definition follows the notation: + +```lu +@ [hasRole[s]] role1, role2, ... +``` + +Shown below are the variety of ways you can explicitly define entities and their roles: + +```lu +> # ml entity definition with roles +> the following are 4 different approaches to define roles: + +@ ml name role1, role2 + +@ ml name hasRoles role1, role2 + +@ ml name +@ name hasRoles role1, role2 + +@ ml name +@ name hasRole role1 +@ name hasRole role2 +``` + +You can also implicitly define roles directly in patterns and labeled utterances using the following format: + +```lu +{@:} +``` + +You can see in the example below how the roles `userName:firstName` and `userName:lastName` are implicitly defined: + +```lu +# getUserName +- My first name is {@userName:firstName=vishwac} +- My full name is {@userName:firstName=vishwac} {@userName:lastName=kannan} +- Hello, I'm {@userName:firstName=vishwac} +- {@userName=vishwac} is my name + +@ ml userName +``` + +In [patterns](#patterns), you can use roles using the `{:}` notation. Here's an example: + +```lu +# getUserName +- call me {name:userName} +- I'm {name:userName} +- my name is {name:userName} +``` + +You can also define multiple roles for an entity in patterns, seen below: + +```lu +> Roles can be specified for list entity types as well - in this case fromCity and toCity are added as roles to the 'city' list entity defined further below + +# BookFlight +- book flight from {city:fromCity} to {city:toCity} +- [can you] get me a flight from {city:fromCity} to {city:toCity} +- get me a flight to {city:toCity} +- I need to fly from {city:fromCity} + +$city:Seattle= +- Seattle +- Tacoma +- SeaTac +- SEA + +$city:Portland= +- Portland +- PDX +``` + +## Patterns + +[Patterns][] allow you to cover a large number of examples that should be matched by creating an utterance with place holders for where entities should be found. The patterns are a token level regular expression with place holders for entities. If an utterance has any entity place holders or pattern syntax then it's interpreted as a pattern. Otherwise, it's interpreted as an utterance for training machine learning. + +The entity place holders can correspond to entities of any type or they can be defined by the pattern itself, such as when a section in the pattern is an entity that is identified by looking at the surrounding words. + +### Pattern syntax + +The .lu file format supports the LUIS [Pattern syntax][]. Pattern syntax is a template embedded in an utterance. The template should contain both words and entities you want to match, as well as words and punctuation you want to ignore. The template isn't a regular expression. + +Entities in patterns are surrounded by braces, {}. Patterns can include entities, and entities with roles. [Pattern.any][pattern-any] is an entity only used in patterns. + +| Function | Syntax | Nesting level | Example | +|:-|:-|:-|:-| +| entity | {} - braces | 2 | `Where is form {entity-name}?` | +| optional | [] - square brackets
There is a limit of 3 on nesting levels of any combination of optional and grouping | 2 | `The question mark is optional [?]` | +| grouping | () - parentheses | 2 | `is (a \| b)` | +| or | \| - vertical bar (pipe)
There is a limit of 2 on the vertical bars (Or) in one group | - | `Where is form ({form-name-short} \| {form-name-long} \| {form-number})` | +| beginning and/or end of utterance | ^ - caret | - | `^begin the utterance`
`the utterance is done^`
`^strict literal match of entire utterance with {number} entity^` | + +See the [Pattern syntax][] article in the LUIS documentation for more information. + +The following example shows a definition that would be treated as a pattern with an `alarmTime` entity defined by the pattern: + +```lu +# DeleteAlarm +- delete the {alarmTime} alarm +``` + +The utterance "delete the 7am alarm" would match the pattern and would recognize an `alarmTime` entity of "7am". + +By contrast, the following example is a _labeled_ utterance where `alarmTime` is a machine-learned entity since it has a labeled value _7AM_: + +```lu +# DeleteAlarm +- delete the {alarmTime=7AM} alarm +``` + +You can't mix entity labels and entity place holders in the same utterance, but you can use place holders that correspond to machine-learned entities. + +> [!TIP] +> You should understand how your bot responds to user input before adding patterns, because patterns are weighted more heavily than example utterances and will skew confidence. There is no harm adding them in the beginning of your model design, but it's easier to see how each pattern changes the model after the model is tested with utterances. + + + +## Phrase list + +A [phrase list][phrase-list] is a list of words or phrases that help find the concept you're trying to identify. The list isn't case-sensitive. Phrase lists have two different purposes: + +- **Extend the dictionary**: This is the default when you define a phrase list and is known as _non-interchangeable_. Multi-word phrases become a feature to the machine learning which requires fewer examples to learn. In this usage, there is no relationship between the members of the phase list. +- **Define synonyms**: Interchangeable phrase lists are used to define synonyms that mean _the same thing_. This usage helps generalize with fewer examples. Any phrase in the list results in the same feature to the machine learning. To use this requires specifying `interchangeable` in your phrase list definition (`@ phraselist (interchangeable)`) + +>[!NOTE] +> a _feature_ can be a phrase list or entity that you associate with an intent or entity to emphasize the importance of that feature in accurately detecting user intent. See [Add a phrase list as a feature](#add-a-phrase-list-as-a-feature) for more information. + +For additional information about when and how to use phrase lists including typical scenarios they're used for, see [Create a phrase list for a concept][phrase-list]. + +You define phrase lists using the following notation: + +```lu +@ phraselist + - + - +``` + +Here's an example of a phrase list used to extend the dictionary: + +```lu +@ phraseList newTerms= +- surf the sky +- jump on the beam +- blue sky pajamas +``` + +Phrase lists can also be used to define synonyms by marking them as interchangeable. + +```lu +@ phraseList Want(interchangeable) = + - require, need, desire, know + +> You can also break up the phrase list values into a bulleted list +@ phraseList Want(interchangeable) = + - require + - need + - desire + - know +``` + +By default, phrase lists are available to all learned intents and entities. There are three availability states: + +| Availability State | Description | +| -------------------- | ----------------------------------------------------------------------------------------------- | +| enabledForAllModels | (default) When a phrase list is marked as `enabledForAllModels`, it's available to all models whether or not you specifically list it as a feature. | +| disabledForAllModels | When a phrase list is marked as `disabledForAllModels`, it's only used in a model if it's specifically listed as a feature. | +| disabled | When a phrase list is marked as `disabled`, it isn't used anywhere, including any models where it's specifically listed as a feature. This provides an easy means to turn off a phrase list to see how well things work without it. | + +Phrase lists are globally available by default, and can also be specifically set using the `enabledForAllModels` keyword: + +```lu +@ phraselist abc enabledForAllModels +``` + +Two examples of setting a phrase list to `disabledForAllModels`: + +```lu +@ phraselist abc disabledForAllModels + +> You can also use this approach +@ phraselist question(interchangeable) = + - are you + - you are + +@ question disabledForAllModels +``` + +When setting a phrase list to `disabled`, it won't be used, even when specifically listed as a feature: + +```lu +> phrase list definition, temporarily set to disabled to measure its impact + +@ phraselist yourPhraseList disabled + +> phrase list as feature to intent, won't be used + +@ intent yourIntent usesFeature yourPhraseList +``` + +Phrase lists can be used as features for specific intents and entities as described in the next section. + +## Adding features to intents and entities + +Machine learning works by taking features and learning how they relate to the desired intent or entity from example utterances. By default, features are simply the words that make up utterances. Phrase lists provide a means to group together multiple words into a new feature; this makes the machine learning generalize better from fewer examples. By default, phrase lists are global and apply to all machine-learned models, but you can also tie them to specific intents or entities. You can also use intents or entities as features to detect other intents as entities. This provides modularity so that you can build up more complex concepts from simpler building blocks. + +>[!NOTE] +> In machine learning, a feature is text that describes a distinguishing trait or attribute of data that your system observes and learns from. Phrase lists, intents, and entities can be used as features as explained in this and the following sections. + +Features can be added to any learned intent or entity using the `usesFeature` keyword. + +### Add a phrase list as a feature + +Phrase lists can be added as a feature to intents or entities. This helps those specific intents or entities without affecting other intents and entities. Here's an example of how to define a phrase list as a feature to another model: + +```lu +> phrase list definition + +@ phraseList PLCity(interchangeable) = + - seattle + - space needle + - SEATAC + - SEA + +> phrase list as feature to intent + +@ intent getUserProfileIntent usesFeature PLCity + +> phrase list as a feature to an ml entity + +@ ml myCity usesFeature PLCity + +@ regex regexZipcode = /[0-9]{5}/ + +> a phrase list is used as a feature in a hierarchal entity + +@ ml address fromAddress, toAddress +@ address = + - @ number 'door number' + - @ ml streetName + - @ ml location + - @ ml city usesFeature PLCity + - @ regexZipcode zipcode +``` + +### Add an entity or intent as a feature + +Below are examples of how to add intents and entities as a feature with `usesFeature`: + +```lu +> entity definition - @ [] + +@ prebuilt personName +@ prebuilt age + +> entity definition with roles + +@ ml userName hasRoles fistName, lastName + +> add an entity as a feature to another entity + +@ userName usesFeature personName + +> add an entity as feature to an intent + +@ intent getUserNameIntent usesFeature personName + +> Intent definition + +# getUserNameIntent +- utterances + +> multiple entities as a feature to a model + +@ intent getUserNameIntent usesFeature age, personName + +> intent as a feature to another intent + +@ intent getUserProfileIntent usesFeature getUserNameIntent + +# getUserProfileIntent +- utterances +``` + +## Metadata + + + +You can include metadata related to your LUIS application or QnA Maker knowledge base in the .lu file. This will help direct the parser to handle the LU content correctly. Metadata is typically added to the beginning of the .lu file. + +Here's how to define configuration information using **> !#**: + +```lu +> !# @ = +> !# @. = +> !# @. = +``` + +Note that any information explicitly passed in via CLI arguments will override information in the .lu file. + +```lu +> LUIS application information +> !# @app.name = my luis application +> !# @app.desc = description of my luis application +> !# @app.versionId = 1.0 +> !# @app.culture = en-us +> !# @app.luis_schema_version = 7.0.0 +> !# @app.settings.NormalizePunctuation = true +> !# @app.settings.NormalizeWordForm = true +> !# @app.settings.UseAllTrainingData = true +> !# @app.tokenizerVersion = 1.0.0 +``` + +See the table below for a description of the application metadata values used in the above example. For information on app.settings in LUIS, see [App and version settings][luis-metadata] in the LUIS documentation. + +| Metadata | Description | +| -------------- | ------------------------------------- | +| Name | Your application name | +| VersionId | The name of that specific version | +| Culture | The language used by your application | +| Schema Version | The LUIS schema is updated anytime a new feature or setting is added in LUIS. Use the schema version number that you used when creating or updating your LUIS model. | + +## External references + +The sections below detail how to make [local file](#local-file-references) and [URI](#uri-references) references. + +### Local file references + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +References the .lu file. Follow Markdown link syntax. Supported references include: + +- Reference to another .lu file via `[link name](<.lu file name>)`. Reference can be an absolute path or a relative path from the containing .lu file. +- Reference to a folder with other .lu files is supported through: + - `[link name](<.lu file path>*)`: looks for .lu files under the specified absolute or relative path + - `[link name](<.lu file path>**)`: recursively looks for .lu files under the specified absolute or relative path, including subfolders. +- You can also add references to utterances defined in a specific file under an intent section or as QnA pairs. + - `[link name](<.lu file path>#)`: finds all utterances under \ in the .lu file and adds them to the list of utterances where the reference is specified. + - `[link name](<.lu file path>#*utterances*)`: finds all utterances (not patterns) under \ in the .lu file and adds them to the list of utterances where the reference is specified. + - `[link name](<.lu file path>#*patterns*)`: finds all patterns (not utterances) under \ in the .lu file and adds them to the list of patterns where the reference is specified. + - `[link name](<.lu file path>#*utterances*)`: finds all utterances in the .lu file and adds them to the list of utterances where the reference is specified. + - `[link name](<.lu file path>#*patterns*)`: finds all patterns in the .lu file and adds them to the list of utterances where the reference is specified. + - `[link name](<.lu file path>#*utterancesAndPatterns*)`: finds all utterances and patterns in the .lu file and adds them to the list of utterances where the reference is specified. + - `[link name](<.qna file path>#$name?)`: finds all alterations from the specific alteration definition in the .qna content and adds them to the list of utterances where the reference is specified. + - `[link name](<.qna file path>#*alterations*?)`: finds all alterations from the .qna content and adds them to the list of utterances where the reference is specified. + - `[link name](<.qna file path>#?question-to-find?)`: finds all variation questions from the specific question and adds them to the list of utterances where the reference is specified. Note that any spaces in your question will need to be replaced with the **-** character. + - `[link name](<.qna file path>#*answers*?)`: finds all answers and adds them to the list of utterances where the reference is specified. + +Here's an example of the aforementioned references: + +```lu +> You can include references to other .lu files + +[All LU files](./all.lu) + +> References to other files can have wildcards in them + +[en-us](./en-us/*) + +> References to other lu files can include subfolders as well. +> /** indicates to the parser to recursively look for .lu files in all subfolders as well. + +[all LU files](../**) + +> You can include deep references to intents defined in a .lu file in utterances + +# None +- [None uttearnces](./all.lu#Help) + +> With the above statement, the parser will parse all.lu and extract out all utterances associated with the 'Help' intent and add them under 'None' intent as defined in this file. + +> NOTE: This **only** works for utterances as entities that are referenced by the uttearnces in the 'Help' intent won't be brought forward to this .lu file. + +# All utterances +> you can use the *utterances* wild card to include all utterances from a lu file. This includes utterances across all intents defined in that .lu file. +- [all.lu](./all.lu#*utterances*) +> you can use the *patterns* wild card to include all patterns from a lu file. +> - [all.lu](./all.lu#*patterns*) +> you can use the *utterancesAndPatterns* wild card to include all utterances and patterns from a lu file. +> - [all.lu](./all.lu#*utterancesAndPatterns*) + +> You can include wild cards with deep references to QnA maker questions defined in a .qna file in utterances + +# None +- [QnA questions](./*#?) + +> With the above statement, the parser will parse **all** .lu files under ./, extract out all questions from QnA pairs in those files and add them under 'None' intent as defined in this file. + +> You can include deep references to QnA maker questions defined in a .qna file in utterances + +# None +- [QnA questions](./qna1.qna#?) + +> With the above statement, the parser will parse qna1.lu and extract out all questions from QnA pairs in that file and add them under 'None' intent as defined in this file. +``` + +### URI references + +Below are examples of how to make URI references: + +```lu +> URI to LU resource +[import](http://.../foo.lu) + +# intent1 +> Ability to pull in specific utterances from an intent +- [import](http://.../foo.lu#None) + +# intent2 +> Ability to pull in utterances or patterns or both from a specific intent 'None' +- [import](http://..../foo.lu#None*utterances*) +- [import](http://..../bar.lu#None*patterns*) +- [import](http://..../taz.lu#None*utterancesandpatterns*) + +# intent3 +> Ability to pull in all utterances or patterns or both across all intents +- [import](http://..../foo.lu#*utterances*) +- [import](http://..../bar.lu#*patterns*) +- [import](http://..../taz.lu#*utterancesandpatterns*) +``` + +## Additional Information + +- Read [.qna file format](bot-builder-qna-file-format.md) for more information about .qna files. + +[luis]: https://luis.ai +[entity]: /azure/ai-services/luis/concepts/entities +[ml-entity]: /azure/ai-services/luis/concepts/entities +[orchestrator]: /composer/concept-orchestrator +[Composer]: /composer/ diff --git a/articles/file-format/bot-builder-qna-file-format.md b/articles/file-format/bot-builder-qna-file-format.md new file mode 100644 index 000000000..55f72ac7d --- /dev/null +++ b/articles/file-format/bot-builder-qna-file-format.md @@ -0,0 +1,272 @@ +--- +title: .qna file format +description: .qna file format reference +keywords: qna file format, reference, qnamaker +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# .qna file format + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +.qna files contain Markdown-like text based definitions for [QnAmaker.ai](http://qnamaker.ai) concepts. This article covers the various concepts expressed via the .qna file format. + +## Adding comments + +Use **>** to create a comment. Here's an example: + +```qna +> This is a comment and will be ignored +``` + +## Question and Answer pairs + +The .qna file and parser support question and answer definitions. + +Here's the syntax of a basic question and answer definition: + +````qna +# ? Question +[list of question variations] +``` +Answer +``` +```` + +Here's are examples of question and answer definitions: + +````qna +> # QnA Definitions +### ? who is the ceo? +``` +You can change the default message if you use the QnAMakerDialog. +For details, see [Azure AI Bot Service documentation](/articles/adaptive-dialog/adaptive-dialog-prebuilt-actions.md). +``` + + +### ? How do I programmatically update my KB? +``` +You can use our REST apis to manage your KB. +\#1. See here for details: https://westus.dev.cognitive.microsoft.com/docs/services/58994a073d9e04097c7ba6fe/operations/58994a073d9e041ad42d9baa +``` +```` + +Note that the `markdown` type identifier for an `answer` is optional. + +### Multiple questions + +You can add multiple questions to the same answer by simply adding variations to questions. + +````qna +### ? Aren't you feeling happy today? +- Feeling cheerful? +```markdown +I'm quite happy, thank you. +``` +```` + +## QnAMaker Filters + +Filters in QnA Maker are simple key-value pairs that can be used to narrow search results, boost answers and store context. + +Use the following syntax to add filters: + +```qna +***Filters:*** +- name = value +- name = value +``` + +Here's an example of how a filter could be used: + +````qna +### ? Where can I get coffee? +- I need coffee + +**Filters:** +- location = seattle + +```markdown +You can get coffee in our Seattle store at 1 pike place, Seattle, WA +``` + +### ? Where can I get coffee? +- I need coffee + +**Filters:** +- location = portland + +```markdown +You can get coffee in our Portland store at 52 marine drive, Portland, OR +``` +```` + +## QnA Maker PDF file ingestion + +QnA Maker also supports ingesting PDF files during KB creation. You can add files for QnA Maker to ingest using the URL reference scheme. If the URI's content type isn't text or HTML, then the parser will add it to files collection for QnA Maker to ingest. + +```qna +[SurfaceManual.pdf](https://download.microsoft.com/download/2/9/B/29B20383-302C-4517-A006-B0186F04BE28/surface-pro-4-user-guide-EN.pdf) +``` + +## External references + +External references are supported in the .qna file and use Markdown link syntax. + +### Reference another .qna file + +Reference to another .qna file using `[link name](<.qna file name>)`. References can be an absolute path or a relative path from the containing .qna file. + +### Reference to a folder containing .qna files + +Reference to a folder with other .qna files is supported through: + +- `[link name](<.qna file path>/*)`: looks for .qna files under the specified absolute or relative path. +- `[link name](<.qna file path>/**)`: recursively looks for .qna files under the specified absolute or relative path including subfolders. + +### Reference a URL + +Reference a URL for QnAMaker to ingest during KB creation via `[link name]()`. + +### Reference from a specific file + +You can also add references to utterances defined in a specific file under an intent section or as QnA pairs. + +- `[link name](<.lu file path>#)`: finds all utterances found under \ in the .lu file and adds them to the list of questions where the reference is specified. +- `[link name](<.lu file path>#*utterances*)`: finds all utterances in the .lu file and adds them to the list of questions where the reference is specified. +- `[link name](<.qna file path>#?)`: finds questions from all QnA pairs defined in the .qna file and adds them to the list of utterances where this reference is specified. +- `[link name](<.qna folder>/*#?)`: finds all questions from all .qna files in the specified folder and adds them to the list of utterances where this reference is specified. + +Here's an example of the above references: + +```qna +> QnA URL reference +[QnaURL](/azure/ai-services/qnamaker/) + +> Include all content in ./kb1.qna +[KB1](./kb1.qna) + +> Look for all .qna files under a path +[ChitChat](./chitchat/*) + +> Recursively look for .qna files under a path including subfolders. +[ChitChat](../chitchat/resources/**) +``` + +## Model description + +You can include configuration information for your LUIS application or QnA Maker KB in the .qna file to help direct the parser to handle the LU content correctly. + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + +Here's how to add configuration information sing **> !#**: + +```qna +> !# @ = +> !# @- = +> !# @- = +``` + +Note that any information explicitly passed in via CLI arguments will override information in the .qna file. + +```qna +> Parser instruction - this is optional; unless specified, the parser will default to the latest version. +> !# @version = 1.0 + +> QnA Maker KB description +> !# @kb.name = my qna maker kb name + +> Source for a specific QnA pair +> !# @qna.pair.source = +``` + +## Multiturn content + +Multiturn content is represented in .qna format using Markdown link notation. Links are specified using the following way: + +```qna +- [display text](#) +``` + +You can optionally include `context-only` for any prompts that are only contextually available for a question. Read the section about [adding an existing question-and-answer pair as a follow-up prompt](/azure/ai-services/language-service/question-answering/tutorials/guided-conversations#add-question-pair-with-follow-up-prompts) to learn more about use of `context`. + +```qna +- [tell me a joke](#?joke) `context-only` +``` + +### Follow-up prompts + +Developers have two options for creating follow-up prompts: using a question as a follow-up prompt directly, or assigning an explicit ID to a QnA pair. + +### Use a question directly + +The first QnA pair that has the link text as a `question` will be added as the prompt. If you need more explicit control, use [IDs](#question-and-answer-pairs) instead. + +When you're directly using a question, use Markdown convention and replace spaces with hyphens (for example, use `#?when-is-the-portland-store-open` instead of `#?when is the portland store open`). The parser will do its best to find the link. + +````qna +# ?store hours +``` +Most our stores are open M-F 9AM-10PM. +``` +**Prompts:** +- [Seattle store](#?seattle) +- [Portland store](#?when-is-the-portland-store-open) + +# ?seattle +``` +The Seattle store is open M-F 9AM-10PM. +``` + +# ?when is the portland store open +- portland store hours +``` +The Portland store is open 24/7. +``` +```` + +> [!TIP] +> The link won't actually render as a selectable link in most Markdown renderers. + +### Assign an explicit ID to a QnA pair + +Assign IDs for each prompt with a number. You can see in the example below the prompt for each store has been assigned a different numeric value. + +````qna +# ?store hours +``` +Most our stores are open M-F 9AM-10PM. +``` +**Prompts:** +- [Seattle store](#1) +- [Portland store](#2) + + + +# ?seattle +``` +The Seattle store is open M-F 9AM-10PM. +``` + + + +# ?when is the portland store open +- portland store hours +``` +The Portland store is open 24/7. +``` +```` + +## Additional Resources + +- See [.lu file format](bot-builder-lu-file-format.md) for information about the .lu file format. diff --git a/articles/how-to-deploy-china-cloud.md b/articles/how-to-deploy-china-cloud.md new file mode 100644 index 000000000..39e1d30e8 --- /dev/null +++ b/articles/how-to-deploy-china-cloud.md @@ -0,0 +1,162 @@ +--- +title: Deploy Bots to Microsoft Azure operated by 21Vianet +description: Learn how to configure a bot to operate in the Microsoft Azure operated by 21Vianet. +author: singhvikra-micro +ms.author: singhvikra +manager: kunsingh +ms.reviewer: kparihar +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +--- + +# Configure a bot in Microsoft Azure operated by 21Vianet + +This guide helps Microsoft Azure customers deploy Bot Framework and Azure AI Bot Service bots on the Microsoft Azure platform operated by 21Vianet. + +## Prerequisites + +- An account in the Microsoft Azure. +- The C# or JavaScript bot project you want to configure. +- Bot Framework SDK version 4.14 or later. + +## Use the cloud adapter + +Make sure that your bot uses the _cloud adapter_, or an adapter that derives from the cloud adapter. +The cloud adapter lets you specify settings specific to the Microsoft Azure Cloud. + +### [C#](#tab/csharp) + +The `ConfigurationBotFrameworkAuthentication` class reads authentication settings from your bot configuration file. Upon creation, the cloud adapter utilizes these authentication settings. + +Make sure that the `ConfigureServices` method in your **Startup.cs** file contains this line. + +```csharp +services.AddSingleton(); +``` + +### [JavaScript](#tab/javascript) + +The `ConfigurationBotFrameworkAuthentication` constructor reads authentication settings from your bot configuration file. + +In your **index.js** file, the code to create your adapter should look like this: + +```javascript +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env); + +const adapter = new CloudAdapter(botFrameworkAuthentication); +``` + +--- + +## Configure UserAssignedMSI/SingleTenant Bot + +To ensure the bot functions correctly in the Microsoft Azure Cloud, extra authentication settings are necessary. Replace the "App-Tenant-ID" with the bot's tenant ID. + +### [C#](#tab/csharp) + +Add the following settings to your **appsettings.json** file. + +```json +"OAuthUrl": "https://token.botframework.azure.cn/", +"ToChannelFromBotLoginUrl": "https://login.partner.microsoftonline.cn/", +"ToChannelFromBotOAuthScope": "https://api.botframework.azure.cn", +"ToBotFromChannelTokenIssuer": "https://api.botframework.azure.cn", +"ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.azure.cn/v1/.well-known/openidconfiguration", +"ToBotFromEmulatorOpenIdMetadataUrl": "https://login.partner.microsoftonline.cn/a55a4d5b-9241-49b1-b4ff-befa8db00269/v2.0/.well-known/openid-configuration", +"ValidateAuthority": true +``` + +### [JavaScript](#tab/javascript) + +Add the following settings to your **.env** file. + +```ini +OAuthUrl=https://token.botframework.azure.cn/, +ToChannelFromBotLoginUrl=https://login.partner.microsoftonline.cn/, +ToChannelFromBotOAuthScope=https://api.botframework.azure.cn, +ToBotFromChannelTokenIssuer=https://api.botframework.azure.cn, +ToBotFromChannelOpenIdMetadataUrl=https://login.botframework.azure.cn/v1/.well-known/openidconfiguration, +ToBotFromEmulatorOpenIdMetadataUrl=https://login.partner.microsoftonline.cn/a55a4d5b-9241-49b1-b4ff-befa8db00269/v2.0/.well-known/openid-configuration, +ValidateAuthority=true +``` + +--- + +## Configure MultiTenant Bot + +For the multitenant bot, use the following settings. + +> [!IMPORTANT] + +> - **Multi-tenant bot creation will be deprecated after July 31, 2025.** +> - Existing multi-tenant bots will continue to function, but new multi-tenant bot creation will no longer be supported after that date. +> - To ensure continued support, use **single-tenant** or **user-assigned managed identity** going forward. + +### [C#](#tab/csharp) + +Add the following settings to your **appsettings.json** file. + +```json +"OAuthUrl": "https://token.botframework.azure.cn/", +"ToChannelFromBotLoginUrl": "https://login.partner.microsoftonline.cn/microsoftservices.partner.onmschina.cn", +"ToChannelFromBotOAuthScope": "https://api.botframework.azure.cn", +"ToBotFromChannelTokenIssuer": "https://api.botframework.azure.cn", +"ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.azure.cn/v1/.well-known/openidconfiguration", +"ToBotFromEmulatorOpenIdMetadataUrl": "https://login.partner.microsoftonline.cn/a55a4d5b-9241-49b1-b4ff-befa8db00269/v2.0/.well-known/openid-configuration", +"ValidateAuthority": true +``` + +### [JavaScript](#tab/javascript) + +Add the following settings to your **.env** file. + +```ini +OAuthUrl=https://token.botframework.azure.cn/, +ToChannelFromBotLoginUrl=https://login.partner.microsoftonline.cn/microsoftservices.partner.onmschina.cn, +ToChannelFromBotOAuthScope=https://api.botframework.azure.cn, +ToBotFromChannelTokenIssuer=https://api.botframework.azure.cn, +ToBotFromChannelOpenIdMetadataUrl=https://login.botframework.azure.cn/v1/.well-known/openidconfiguration, +ToBotFromEmulatorOpenIdMetadataUrl=https://login.partner.microsoftonline.cn/a55a4d5b-9241-49b1-b4ff-befa8db00269/v2.0/.well-known/openid-configuration, +ValidateAuthority=true +``` + +--- + +## Add user authentication to your bot + +Your bot can use various identity providers to access resources on behalf of a user, such as Microsoft Entra ID and many other OAuth providers. + +The Microsoft Azure Cloud uses a redirect URL that is different from the ones used for other environments. +To configuring your bot for authentication, use `https://token.botframework.azure.cn/.auth/web/redirect` as the OAuth redirect URL and follow the steps in how to [add authentication to your bot](v4sdk/bot-builder-authentication.md). + +--- + +## Configure a bot to run on one or more channels + +To configure a bot to connect to a channel, complete the following steps: + +1. Sign in to the [Azure portal](https://portal.azure.cn). +2. Select the bot that you want to configure. +3. In the left pane, select **Channels** under **Settings**. +4. In the right pane, select the icon of the channel you want to add to your bot. You may need to scroll down to see the list of all **Available Channels**. The connection steps vary for each channel. For more information on supported channels, see the related articles. + +| Channel | Description | +|:-|:-| +| [Direct Line](bot-service-channel-directline.md) | Integrate a bot into a mobile app, web page, or other applications. | +| [Microsoft Teams](channel-connect-teams.md) | Configure a bot to communicate with users through Microsoft Teams. | +| [Web Chat](bot-service-channel-connect-webchat.md) | Automatically configured for you when you create a bot with the Bot Framework Service. | + +--- + +## Next steps + +With these steps, your bot should be configured to work successfully. Other useful references regarding Bot Service. + +- [Tutorial: Deploy a basic bot using Azure AI Bot Service](tutorial-publish-a-bot.md) +- [Add authentication to a bot in Bot Framework SDK](v4sdk/bot-builder-authentication.md) +- [Connect a bot to Web Chat in the Bot Framework SDK](bot-service-channel-connect-webchat.md) +- [Authenticate requests with the Bot Connector API](rest-api/bot-framework-rest-connector-authentication.md) +- [Compliance in the Azure AI Bot Service](bot-service-compliance.md) diff --git a/articles/how-to-deploy-gov-cloud-high.md b/articles/how-to-deploy-gov-cloud-high.md new file mode 100644 index 000000000..05804f9ca --- /dev/null +++ b/articles/how-to-deploy-gov-cloud-high.md @@ -0,0 +1,184 @@ +--- +title: Deploy Bots to Azure Government and Office 365 GCC High +description: Learn how to configure a bot to operate in the Microsoft Azure Government cloud and the Microsoft Office 365 Government Community Cloud (GCC) High environment. +author: jameslew +ms.author: iawilt +manager: shellyha +ms.reviewer: jameslew +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - evergreen +--- + +# Configure Bot Framework bots for US Government customers + +This article is for US government customers who are deploying Bot Framework and Azure AI Bot Service bots to the Microsoft Azure Government cloud. + +> [!TIP] +> Bots in Azure Government that connect to Microsoft Teams must use the Microsoft Office 365 Government Community Cloud (GCC) High environment. + +This article describes how to configure a bot to work with the Azure Government cloud and with the Office 365 GCC High environment. + +## Prerequisites + +- An account in the Azure Government cloud. +- To extend Teams, an Azure Bot resource created in the Office 365 GCC High environment. +- The C# or JavaScript bot project you want to configure. +- Bot Framework SDK version 4.14 or later. + +## Use the cloud adapter + +Make sure that your bot uses the _cloud adapter_, or an adapter that derives from the cloud adapter. +The cloud adapter lets you specify settings specific to the Azure Government cloud and the Office 365 GCC High environment. + +### [C#](#tab/csharp) + +The `ConfigurationBotFrameworkAuthentication` class reads authentication settings from your bot configuration file. +The cloud adapter, when it's created, will use these authentication settings. + +Make sure that the `ConfigureServices` method in your **Startup.cs** file contains this line. + +```csharp +services.AddSingleton(); +``` + +### [JavaScript](#tab/javascript) + +The `ConfigurationBotFrameworkAuthentication` constructor reads authentication settings from your bot configuration file. + +In your **index.js** file, the code to create your adapter should look like this: + +```javascript +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env); + +const adapter = new CloudAdapter(botFrameworkAuthentication); +``` + +--- + +## Configure for Azure Government + +The Azure Government cloud uses `https://botframework.azure.us` for the channel service endpoint. +For most channels, setting the channel service endpoint is sufficient. + +See the next section for additional settings required to support Microsoft Teams in the Office 365 GCC High environment. + +### [C#](#tab/csharp) + +Add the following setting to your **appsettings.json** file. + +```json +"ChannelService": "https://botframework.azure.us", +``` + +### [JavaScript](#tab/javascript) + +Add the following setting to your **.env** file. + +```ini +ChannelService=https://botframework.azure.us +``` + +--- + +## Configure for Office 365 GCC High + +For Office 365 services, additional settings are required to handle user authentication correctly. +Currently, only the Microsoft Teams channel is available in the Office 365 GCC High environment. + +### [C#](#tab/csharp) + +Add the following settings to your **appsettings.json** file. + +```json +"ChannelService": "https://botframework.azure.us", +"OAuthUrl": "https://tokengcch.botframework.azure.us/", +"ToChannelFromBotLoginUrl": "https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us", +"ToChannelFromBotOAuthScope": "https://api.botframework.us", +"ToBotFromChannelTokenIssuer": "https://api.botframework.us", +"ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.azure.us/v1/.well-known/openidconfiguration", +"ToBotFromEmulatorOpenIdMetadataUrl": "https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0/.well-known/openid-configuration", +"ValidateAuthority": true, +``` + +### [JavaScript](#tab/javascript) + +Add the following settings to your **.env** file. + +```ini +ChannelService=https://botframework.azure.us +OAuthUrl=https://tokengcch.botframework.azure.us/ +ToChannelFromBotLoginUrl=https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us +ToChannelFromBotOAuthScope=https://api.botframework.us +ToBotFromChannelTokenIssuer=https://api.botframework.us +ToBotFromChannelOpenIdMetadataUrl=https://login.botframework.azure.us/v1/.well-known/openidconfiguration +ToBotFromEmulatorOpenIdMetadataUrl=https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0/.well-known/openid-configuration +ValidateAuthority=true +``` + +--- + +## Configure for DoD environment + +There is also a **DoD environment** which shares most (but not all) settings with the Office 365 GCC High environment. For the DoD environment use the following settings. + +### [C#](#tab/csharp) + +Add the following settings to your **appsettings.json** file. + +```json +"ChannelService": "https://botframework.azure.us", +"OAuthUrl": "https://apiDoD.botframework.azure.us", +"ToChannelFromBotLoginUrl": "https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us", +"ToChannelFromBotOAuthScope": "https://api.botframework.us", +"ToBotFromChannelTokenIssuer": "https://api.botframework.us", +"ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.azure.us/v1/.well-known/openidconfiguration", +"ToBotFromEmulatorOpenIdMetadataUrl": "https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0/.well-known/openid-configuration", +"ValidateAuthority": true, +``` + +### [JavaScript](#tab/javascript) + +Add the following settings to your **.env** file. + +```ini +ChannelService=https://botframework.azure.us +OAuthUrl=https://apiDoD.botframework.azure.us +ToChannelFromBotLoginUrl=https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us +ToChannelFromBotOAuthScope=https://api.botframework.us +ToBotFromChannelTokenIssuer=https://api.botframework.us +ToBotFromChannelOpenIdMetadataUrl=https://login.botframework.azure.us/v1/.well-known/openidconfiguration +ToBotFromEmulatorOpenIdMetadataUrl=https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0/.well-known/openid-configuration +ValidateAuthority=true +``` + +--- + +## Add user authentication to your bot + +Your bot can use various identity providers to access resources on behalf of a user, such as Microsoft Entra ID and many other OAuth providers. + +The Office 365 GCC High environment uses a redirect URL that is different from the ones used for other environments. +When configuring your bot for authentication within the Office 365 GCC High environment, use `https://tokengcch.botframework.azure.us/.auth/web/redirect` as the OAuth redirect URL and follow the steps in how to [add authentication to your bot](v4sdk/bot-builder-authentication.md). + +## Additional information + +For more information about Microsoft Azure Government and Office 365 Government High, see: + +- [What is Azure Government?](/azure/azure-government/documentation-government-welcome) +- [Office 365 Government High and DoD](/office365/servicedescriptions/office-365-platform-service-description/office-365-us-government/gcc-high-and-dod) +- [Teams for Government](/microsoftteams/expand-teams-across-your-org/teams-for-government-landing-page) + +## Next steps + +With these steps your bot should be configured to work successfully in the Azure Government cloud and the Office 365 GCC High environment. +Other useful references regarding Bot Service in Azure Government. + +- [Tutorial: Deploy a basic bot using Azure AI Bot Service](tutorial-publish-a-bot.md) +- [Add authentication to a bot in Bot Framework SDK](v4sdk/bot-builder-authentication.md) +- [Connect a bot to Web Chat in the Bot Framework SDK](bot-service-channel-connect-webchat.md) +- [Authenticate requests with the Bot Connector API](rest-api/bot-framework-rest-connector-authentication.md) +- [Compliance in the Azure AI Bot Service](bot-service-compliance.md) +- [Azure Government Documentation](/azure/azure-government/) diff --git a/articles/includes/alert-await-send-activity.md b/articles/includes/alert-await-send-activity.md deleted file mode 100644 index 44e0fee0f..000000000 --- a/articles/includes/alert-await-send-activity.md +++ /dev/null @@ -1,2 +0,0 @@ -> [!IMPORTANT] -> The thread handling the primary bot turn deals with disposing of the context object when it is done. **Be sure to `await` any activity calls** so the primary thread will wait on the generated activity before finishing its processing and disposing of the turn context. Otherwise, if a response (including its handlers) takes any significant amount of time and tries to act on the context object, it may get a _context was disposed_ error. \ No newline at end of file diff --git a/articles/includes/applies-to-both.md b/articles/includes/applies-to-both.md deleted file mode 100644 index 3f296f7b6..000000000 --- a/articles/includes/applies-to-both.md +++ /dev/null @@ -1 +0,0 @@ -**APPLIES TO:** ![yes](../media/yes.png)SDK v4 ![yes](../media/yes.png)SDK v3 diff --git a/articles/includes/applies-to-v4-current.md b/articles/includes/applies-to-v4-current.md new file mode 100644 index 000000000..fce31315c --- /dev/null +++ b/articles/includes/applies-to-v4-current.md @@ -0,0 +1 @@ +**APPLIES TO:** SDK v4 diff --git a/articles/includes/applies-to.md b/articles/includes/applies-to.md deleted file mode 100644 index 5f03df722..000000000 --- a/articles/includes/applies-to.md +++ /dev/null @@ -1 +0,0 @@ -**APPLIES TO:** ![yes](../media/yes.png)SDK v4 ![no](../media/no.png) SDK v3 diff --git a/articles/includes/authentication/azure-bot-appid-password.md b/articles/includes/authentication/azure-bot-appid-password.md new file mode 100644 index 000000000..22df5437f --- /dev/null +++ b/articles/includes/authentication/azure-bot-appid-password.md @@ -0,0 +1,70 @@ +--- +description: Procedure for adding bot identity information to the bot's configuration file. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + + + +### Bot Identity Information + +Follow these steps to add identity information to your bot's configuration file. The file differs depending on the programming language used to create the bot. + +> [!IMPORTANT] +> +> - The **Java** version of the Bot Framework SDK only supports **multi-tenant** bots. +> - The **C#**, **JavaScript**, and **Python** versions support all three application types for managing the bot's identity. + +| Language | File name | Notes | +|:-----------|:-----------------------|:--------------------------------------------------------------------------------------------------------------------| +| C# | appsettings.json | Supports all three application types for managing your bot's identity. | +| JavaScript | .env | Supports all three application types for managing your bot's identity. | +| Java | application.properties | Only supports multi-tenant bots. | +| Python | config.py | Supports all three application types for managing your bot's identity. | + +The identity information you need to add depends on the bot's application type. Provide the following values in your configuration file. + +#### [User-assigned managed identity](#tab/userassigned) + +Available for C#, JavaScript, and Python bots. + +| Property | Value | +|:-----------------------|:---------------------------------------------------------------------------| +| `MicrosoftAppType` | `UserAssignedMSI` | +| `MicrosoftAppId` | The client ID of the user-assigned managed identity. | +| `MicrosoftAppPassword` | Not applicable. Leave this blank for a user-assigned managed identity bot. | +| `MicrosoftAppTenantId` | The tenant ID of the user-assigned managed identity. | + +#### [Single-tenant](#tab/singletenant) + +Available for C#, JavaScript, and Python bots. + +| Property | Value | +|:-----------------------|:-------------------------| +| `MicrosoftAppType` | `SingleTenant` | +| `MicrosoftAppId` | The bot's app ID. | +| `MicrosoftAppPassword` | The bot's app password. | +| `MicrosoftAppTenantId` | The bot's app tenant ID. | + +#### [Multi-tenant](#tab/multitenant) + +Available for bots in all programming languages: C#, JavaScript, Java, and Python. + +> [!IMPORTANT] +> - **Multi-tenant bot creation will be deprecated after July 31, 2025.** +> - Existing multi-tenant bots will continue to function, but new multi-tenant bot creation will no longer be supported after that date. +> - To ensure continued support, use **single-tenant** or **user-assigned managed identity** going forward. + +| Property | Value | +|:-----------------------|:---------------------------------------------------------| +| `MicrosoftAppType` | `MultiTenant` | +| `MicrosoftAppId` | The bot's app ID. | +| `MicrosoftAppPassword` | The bot's app password. | +| `MicrosoftAppTenantId` | Not applicable. Leave this blank for a multi-tenant bot. | + +--- diff --git a/articles/includes/az-cli/arm-provision-to-existing-rg.md b/articles/includes/az-cli/arm-provision-to-existing-rg.md new file mode 100644 index 000000000..129d62e1f --- /dev/null +++ b/articles/includes/az-cli/arm-provision-to-existing-rg.md @@ -0,0 +1,93 @@ +--- +description: Use Azure CLI to create an application service in an existing resource group. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +> [!IMPORTANT] +> Python bots can't be deployed to a resource group that contains Windows services or bots. +> Multiple Python bots can be deployed to the same resource group; however, you need to create other services (such as Azure AI services) in another resource group. + +### [User-assigned managed identity](#tab/userassigned) + +For a user-assigned managed identity bot, run one of the following commands to provision resources in an existing resource group. + +- To use an _existing_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appType="UserAssignedMSI" tenantId="" existingUserAssignedMSIName="" existingUserAssignedMSIResourceGroupName="" botId="" newWebAppName="" existingAppServicePlan="" appServicePlanLocation="" --name "" + ``` + +- To use a _new_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appType="UserAssignedMSI" tenantId="" existingUserAssignedMSIName="" existingUserAssignedMSIResourceGroupName="" botId="" newWebAppName="" newAppServicePlanName="" appServicePlanLocation="" --name "" + ``` + +### [Single-tenant](#tab/singletenant) + +For a single-tenant bot, run one of the following commands to provision resources in an existing resource group. + +- To use an _existing_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appSecret="" appType="SingleTenant" tenantId="" botId="" newWebAppName="" existingAppServicePlan="" appServicePlanLocation="" --name "" + ``` + +- To use a _new_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appSecret="" appType="SingleTenant" tenantId="" botId="" newWebAppName="" newAppServicePlanName="" appServicePlanLocation="" --name "" + ``` + +### [Multi-tenant](#tab/multitenant) + +For a multi-tenant bot, run one of the following commands to provision resources in an existing resource group. + +- To use an _existing_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appSecret="" botId="" newWebAppName="" existingAppServicePlan="" appServicePlanLocation="" --name "" + ``` + +- To use a _new_ app service plan. + + ```azurecli + az deployment group create --resource-group "" --template-file "" --parameters appId="" appSecret="" botId="" newWebAppName="" newAppServicePlanName="" appServicePlanLocation="" --name "" + ``` + +--- + +This table describes the command _options_ for the `az deployment group create` command. + +| Option | Description | +|:---------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | The deployment name. | +| parameters | Deployment parameters for the ARM template, provided as a list of key-value pairs. See the table of parameters for descriptions. | +| resource-group | Name of the Azure resource group. | +| template-file | The path to the `template-with-preexisting-rg.json` ARM template in the project's _deployment templates_ folder. The path can be relative or absolute. | + +This table describes the _deployment parameters_ to use with the `--parameters` command option. +Not all parameters apply to all app types, and some parameters are specific to using and existing app service or to creating a new app service. + +| Parameter | Description | +|:-|:-| +| appId | The client or app ID from the identity resource you created earlier. This is used as the Microsoft app ID of the web app. | +| appSecret | For single-tenant and multi-tenant app types, the password for the identity resource you created earlier. | +| appServicePlanLocation | The region in which to create the application service plan. | +| appType | The type of app service to create: `UserAssignedMSI`, `SingleTenant`, or `MultiTenant`. Default is `MultiTenant`. | +| botId | The ID of the Azure Bot resource to create. The bot ID is immutable. | +| botSku | Optional, the pricing tier to use for the bot: `F0` (Free) or `S1` (Standard). Default is `F0`. | +| existingAppServicePlan | The name of the existing app service plan to use. | +| existingUserAssignedMSIName | For user-assigned managed identity app types, the name of the identity resource you created earlier. | +| existingUserAssignedMSIResourceGroupName | For user-assigned managed identity app types, the name of the resource group for your identity resource. | +| newAppServicePlanName | The name of the app service plan to create for the bot. | +| newAppServicePlanSku | Optional, the pricing tier to use for the app service plan. Default is `S1`. | +| newWebAppName | The name of the app service to create for the bot. Must be a globally-unique web app name. Default is the value for the `botId` parameter. | +| tenantId | For user-assigned managed identity or single-tenant bots, the bot's app tenant ID. | diff --git a/articles/includes/az-cli/arm-provision-to-new-rg.md b/articles/includes/az-cli/arm-provision-to-new-rg.md new file mode 100644 index 000000000..1b4cb5381 --- /dev/null +++ b/articles/includes/az-cli/arm-provision-to-new-rg.md @@ -0,0 +1,66 @@ +--- +description: Use Azure CLI to create the application service in a new resource group. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +### [User-assigned managed identity](#tab/userassigned) + +For a user-assigned managed identity bot, run this command to provision resources in a new resource group and new app service plan. + +```azurecli +az deployment sub create --template-file "" --location --parameters appType="UserAssignedMSI" appId="" tenantId="" existingUserAssignedMSIName="" existingUserAssignedMSIResourceGroupName="" botId="" botSku= newAppServicePlanName="" newWebAppName="" groupName="" groupLocation="" newAppServicePlanLocation="" --name "" +``` + +### [Single-tenant](#tab/singletenant) + +For a single-tenant bot, run this command to provision resources in a new resource group and new app service plan. + +```azurecli +az deployment sub create --template-file "" --location --parameters appType="SingleTenant" appId="" appSecret="" tenantId="" botId="" botSku= newAppServicePlanName="" newWebAppName="" groupName="" groupLocation="" newAppServicePlanLocation="" --name "" +``` + +### [Multi-tenant](#tab/multitenant) + +For a multi-tenant bot, run this command to provision resources in a new resource group and new app service plan. + +```azurecli +az deployment sub create --template-file "" --location --parameters appType="MultiTenant" appId="" appSecret="" botId="" botSku= newAppServicePlanName="" newWebAppName="" groupName="" groupLocation="" newAppServicePlanLocation="" --name "" +``` + +--- + +This table describes the command _options_ for the `az deployment sub create` command. + +| Option | Description | +|:--------------|:---------------------------------------------------------------------------------------------------------------------------------------------------| +| location | The region in which to create the Azure Bot resource. | +| name | The deployment name. | +| parameters | Deployment parameters for the ARM template, provided as a list of key-value pairs. See the table of parameters for descriptions. | +| template-file | The path to the `template-with-new-rg.json` ARM template in the bot project's _deployment templates_ folder. The path can be relative or absolute. | + +This table describes the _deployment parameters_ to use with the `--parameters` command option. +Not all parameters apply to all app types. + +| Parameter | Description | +|:-|:-| +| appId | The client or app ID from the identity resource you created earlier. This is used as the Microsoft app ID of the web app. | +| appSecret | For for single-tenant and multi-tenant app types, the password for the identity resource you created earlier. | +| appType | The type of app service to create: `UserAssignedMSI`, `SingleTenant`, or `MultiTenant`. Default is `MultiTenant`. | +| botId | The ID of the Azure Bot resource to create. The bot ID is immutable. | +| botSku | The pricing tier to use for the bot: `F0` (Free) or `S1` (Standard). | +| existingUserAssignedMSIName | For user-assigned managed identity app types, the name of the identity resource you created earlier. | +| existingUserAssignedMSIResourceGroupName | For user-assigned managed identity app types, the name of the resource group for your identity resource. | +| groupLocation | The region in which to create your new resource group. | +| groupName | A name for your new resource group. | +| newAppServicePlanLocation | The region in which to create the application service plan. | +| newAppServicePlanName | The name of the app service plan to create for the bot. | +| newAppServicePlanSku | Optional, the pricing tier to use for the app service plan. Default is `S1`. | +| newWebAppName | The name of the app service to create for the bot. Must be a globally-unique web app name. Default is the value for the `botId` parameter. | +| tenantId | For users-assigned managed identity and single-tenant app types, the Microsoft Entra ID tenant to use for bot authentication. Default is your subscription tenant ID. | diff --git a/articles/includes/az-cli/create-identity-multi-tenant.md b/articles/includes/az-cli/create-identity-multi-tenant.md new file mode 100644 index 000000000..e7a9b2c8d --- /dev/null +++ b/articles/includes/az-cli/create-identity-multi-tenant.md @@ -0,0 +1,59 @@ +--- +description: Use Azure CLI to create an application registration for a multi tenant bot. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +### [C# / JavaScript / Python](#tab/csharp+javascript) + +For Azure CLI 2.39.0 or later, use the following commands to create your app registration and set its password. On success, these commands generate JSON output. + +1. Use the `az ad app create` command to create an Microsoft Entra ID app registration. + This command generates an app ID that you'll need in later steps. + + ```azurecli + az ad app create --display-name "" --sign-in-audience "AzureADandPersonalMicrosoftAccount" + ``` + + | Option | Description | + |:-----------------|:------------------------------------------------------------------------------------------| + | display-name | The display name for your app registration. | + | sign-in-audience | The supported Microsoft accounts for the app. Use `AzureADandPersonalMicrosoftAccount` for a multi-tenant app. | + +1. Use the `az ad app credential reset` command to generate a new password for your app registration. + + ```azurecli + az ad app credential reset --id "" + ``` + +1. Record values you'll need in later steps: the _app ID_ and _password_ from the command output. + +For more information about `az ad app`, see the [command reference](/cli/azure/ad/app). For more information about the `sign-in-audience` parameter, see [sigInAudience values](/graph/api/resources/application#signinaudience-values). + +### [Java](#tab/java+python) + +For Azure CLI 2.36.0 or earlier, use the following command to create your app registration and set its password. On success, this command generates JSON output. + +1. Use the `az ad app create` command to create an Microsoft Entra ID app registration. + + ```azurecli + az ad app create --display-name "" --password "" --available-to-other-tenants + ``` + + | Option | Description | + |:-|:-| + | display-name | The display name for the app registration. | + | password | The password, or _client secret_, for the application. It must be at least 16 characters long and contain at least one upper-case or lower-case alphabetical character, at least one numeric character, and at least one special character. | + | available-to-other-tenants | Include this flag to create a multi-tenant bot. It allows the application to be accessible from any Microsoft Entra ID tenant. | + +1. Record values you'll need in later steps. + 1. The password you used + 1. The _app ID_ from the command output + +--- diff --git a/articles/includes/az-cli/create-identity-resource.md b/articles/includes/az-cli/create-identity-resource.md new file mode 100644 index 000000000..fd6c1aa76 --- /dev/null +++ b/articles/includes/az-cli/create-identity-resource.md @@ -0,0 +1,91 @@ +--- +description: Use Azure CLI to create an application registration. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +[!INCLUDE [Note about support for each identity app type](../azure-bot-resource/identity-app-type-support.md)] + +To create an Azure application registration: + +### [User-assigned managed identity](#tab/userassigned) + +> [!TIP] +> You need an existing resource group in which to create the managed identity. + +1. If you don't already have an appropriate resource group, use the `az group create` command to create a new resource group. + + ```azurecli + az group create --name "" --location "" + ``` + + | Option | Description | + |:---------|:--------------------------------------------------| + | name | The name of the resource group to create. | + | location | The region in which to create the resource group. | + + For more information, see [How to manage Azure resource groups with the Azure CLI](/cli/azure/manage-azure-groups-azure-cli). + +1. To create a user-assigned managed identity, use the `az identity create` command. + On success, the command generates JSON output. + + ```azurecli + az identity create --resource-group "" --name "" + ``` + + | Option | Description | + |:---------------|:----------------------------------------------------------------| + | resource-group | The name of the resource group in which to create the identity. | + | name | The name of the identity resource to create. | + + For more information, see the [az identity](/cli/azure/identity) reference. + +1. Record the resource group and identity name you entered and the `clientId` from the command output. + You'll use these values in following steps. + +### [Single-tenant](#tab/singletenant) + +1. Use the `az ad app create` command to create an Microsoft Entra ID app registration. + On success, the command generates JSON output. + + ```azurecli + az ad app create --display-name "" --password "" + ``` + + | Option | Description | + |:-|:-| + | display-name | The display name for the app registration. | + | password | The password, or _client secret_, for the application. It must be at least 16 characters long, contain at least 1 upper or lower case alphabetical character, at least one numeric character, and contain at least 1 special character. | + + For more information, see the [az ad app](/cli/azure/ad/app) reference. + +1. Record the password you entered in the command and the `appId` from the command output. + You'll use these values in following steps. + +### [Multi-tenant](#tab/multitenant) + +1. Use the `az ad app create` command to create an Microsoft Entra ID app registration. + On success, the command generates JSON output. + + ```azurecli + az ad app create --display-name "" --password "" --available-to-other-tenants + ``` + + | Option | Description | + |:-|:-| + | display-name | The display name for the app registration. | + | password | The password, or _client secret_, for the application. It must be at least 16 characters long, contain at least 1 upper or lower case alphabetical character, at least one numeric character, and contain at least 1 special character. | + | available-to-other-tenants | Include this flag to create a multi-tenant bot. It allows the application to be accessible from any Microsoft Entra ID tenant. | + + For more information, see the [az ad app](/cli/azure/ad/app) reference. + +1. Record the password you entered in the command and the `appId` from the command output. + You'll use these values in following steps. + +--- diff --git a/articles/includes/az-cli/deploy-to-azure.md b/articles/includes/az-cli/deploy-to-azure.md new file mode 100644 index 000000000..c400031d7 --- /dev/null +++ b/articles/includes/az-cli/deploy-to-azure.md @@ -0,0 +1,46 @@ +--- +description: Use Azure CLI to deploy your bot files to Azure. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +At this point, you're ready to deploy code for your bot to your App Service resource. + +> [!NOTE] +> This step can take a few minutes to complete. +> Also it can take a few more minutes between when the deployment finishes and when your bot is available to test. + +### [C# / JavaScript / Python](#tab/csharp+javascript+python) + +Run the [`az webapp deploy` command](/cli/azure/webapp#az-webapp-deploy) from the command line to perform deployment using the Kudu zip push deployment for your app service (web app). + +| Option | Description | +|:---------------|:----------------------------------------------------------------------| +| resource-group | The name of the Azure resource group that contains your bot. | +| name | Name of the app service you used earlier. | +| src | The absolute or relative path to the zipped project file you created. | + +> [!TIP] +> By default, this command deploys to the production slot. Use the optional `--slot` parameter to specify a different slot. +> For more information, see the [`az webapp deploy` command reference documentation](/cli/azure/webapp#az-webapp-deploy). + +### [Java](#tab/java) + +In the project directory, run the following command from the command line. + +```console +mvn azure-webapp:deploy -Dgroupname="" -Dbotname="" +``` + +| Option | Description | +|:-----------|:-------------------------------------------------------------| +| Dgroupname | The name of the Azure resource group that contains your bot. | +| Dbotname | Name of the app service you used earlier. | + +--- diff --git a/articles/includes/az-cli/prepare-for-deployment.md b/articles/includes/az-cli/prepare-for-deployment.md new file mode 100644 index 000000000..0e2617f9d --- /dev/null +++ b/articles/includes/az-cli/prepare-for-deployment.md @@ -0,0 +1,74 @@ +--- +description: Describes how to prepare a bot project for deployment. Assumes that the bot has already been provisioned in Azure. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +Prepare your project files before you deploy your bot. + +### [C#](#tab/csharp) + +1. Switch to your project's root folder. For C#, the root is the folder that contains the .csproj file. +1. Do a clean rebuild in _release mode_. +1. If you haven't done so before, run `az bot prepare-deploy` to add required files to the root of your local source code directory. + This command generates a `.deployment` file in your bot project folder. + + ```azurecli + az bot prepare-deploy --lang Csharp --code-dir "." --proj-file-path "" + ``` + + | Option | Description | + |:---------------|:----------------------------------------------------------------------------------------------------------------------------| + | lang | The language or runtime of the bot. Use `Csharp`. | + | code-dir | The directory to place the generated deployment files in. Use your project's root folder. Default is the current directory. | + | proj-file-path | The path to the .csproj file for your bot, relative to the `code-dir` option. | + +1. Within your project's root folder, create a zip file that contains all files and subfolders. + +### [JavaScript](#tab/javascript) + +1. Switch to your project's root folder. + - For _JavaScript_, the root is the folder that contains the app.js or index.js file. + - For _TypeScript_, the root is the folder that contains the _src_ folder (where the bot.ts and index.ts files are). +1. Run `npm install`. +1. If you haven't done so before, run `az bot prepare-deploy` to add required files to the root of your local source code directory. + This command generates a `web.config` file in your project folder. + Azure App Services requires each Node.js bot to include a web.config file in its project root folder. + + ```azurecli + az bot prepare-deploy --lang --code-dir "." + ``` + + | Option | Description | + |:---------|:----------------------------------------------------------------------------------------------------------------------------| + | lang | The language or runtime of the bot. Use `Javascript` or `Typescript`. | + | code-dir | The directory to place the generated deployment files in. Use your project's root folder. Default is the current directory. | + +1. Within your project's root folder, create a zip file that contains all files and subfolders. + +### [Java](#tab/java) + +1. Switch to your project's root folder. +1. In the project directory, run the following command from the command line: + + ```console + mvn clean package + ```` + +### [Python](#tab/python) + +> [!TIP] +> For Python bots, dependency installation is performed on the server. +> The ARM templates require your dependencies to be listed in a `requirements.txt` file. + +1. If you're using a dependency and package manager: + 1. Convert your dependencies list to a `requirements.txt` file + 1. Add `requirements.txt` to the folder that contains `app.py`. +1. Within your project's root folder, create a zip file that contains all files and subfolders. + +--- diff --git a/articles/includes/az-cli/prereqs.md b/articles/includes/az-cli/prereqs.md new file mode 100644 index 000000000..417308dbb --- /dev/null +++ b/articles/includes/az-cli/prereqs.md @@ -0,0 +1,25 @@ +--- +description: Common prerequisites, specific to using the Azure CLI. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +To use the Azure CLI to provision and publish bots, you need: + +- An Azure account that has an active subscription. [Create a free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +- An [install of the Azure CLI](/cli/azure/install-azure-cli). + + For your programming language, use the following version of the Azure CLI. + Some steps won't work with later versions of the CLI. + + | Language | CLI version | + |:------------------|:---------------:| + | C# and JavaScript | 2.39.0 or later | + | Python | 2.55.0 or later | + | Java | 2.29.2 | diff --git a/articles/includes/az-cli/sign-in-select-subscription.md b/articles/includes/az-cli/sign-in-select-subscription.md new file mode 100644 index 000000000..d01bfda59 --- /dev/null +++ b/articles/includes/az-cli/sign-in-select-subscription.md @@ -0,0 +1,36 @@ +--- +description: Azure CLI instructions to sign into Azure and select a subscription. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - devx-track-azurecli + - evergreen +--- + +1. Open a command window. + +1. Sign in to Azure. + + ```azurecli + az login + ``` + + - A browser window will open. Complete the sign-in process. + - On success, the command outputs a list of the subscriptions your account has access to. + +1. To set the subscription to use, run: + + ```azurecli + az account set --subscription "" + ``` + + For \, use the ID or name of the subscription to use. + +1. If you'll create a user-assigned managed identity or a single-tenant bot, record the `tenantId` for the subscription. + You'll use the tenant ID in the following steps. + +> [!TIP] +> If you need to work in a non-public cloud, see [Azure cloud management with the Azure CLI](/cli/azure/manage-clouds-azure-cli). diff --git a/articles/includes/azure-bot-resource/azure-bot-resource.md b/articles/includes/azure-bot-resource/azure-bot-resource.md new file mode 100644 index 000000000..0fae73f87 --- /dev/null +++ b/articles/includes/azure-bot-resource/azure-bot-resource.md @@ -0,0 +1,80 @@ +--- +description: Procedure for creating an Azure Bot resource, in all identity-management flavors. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +## Create the resource + +Create the **Azure Bot** resource, which will allow you to register your bot with the Azure AI Bot Service. + +[!INCLUDE [bot-resource-type-tip](../bot-resource-type-tip.md)] + +1. Go to the [Azure portal](https://portal.azure.com/). +1. In the right pane, select **Create a resource**. +1. In the search box enter `bot`, then press **Enter**. +1. Select the **Azure Bot** card. + + :::image type="content" source="../../media/azure-manage-a-bot/azure-bot-resource.png" alt-text="Select Azure bot resource"::: + +1. Select **Create**. +1. Enter values in the required fields and review and update settings. + + 1. Provide information under **Project details**. Select whether your bot will have global or local data residency. Currently, the local data residency feature is available for resources in the "westeurope" and "centralindia" region. For more information, see [Regionalization in Azure AI Bot Service](../../v4sdk/bot-builder-concept-regionalization.md). + + :::image type="content" source="../../media/azure-bot-resource/azure-bot-project-details.png" alt-text="The project details settings for an Azure Bot resource"::: + + 1. Provide information under **Microsoft App ID**. Select how your bot identity will be managed in Azure and whether to create a new identity or use an existing one. + + :::image type="content" source="../../media/azure-bot-resource/azure-bot-ms-app-id.png" alt-text="The Microsoft app ID settings for an Azure Bot resource"::: + +1. Select **Review + create**. +1. If the validation passes, select **Create**. +1. Once the deployment completes, select **Go to resource**. You should see the bot and related resources listed in the resource group you selected. +1. If you don't already have the Bot Framework SDK, select **Download from GitHub** to learn how to consume the packages for your preferred language. + + :::image type="content" source="../../media/azure-manage-a-bot/azure-bot-create-sdk.png" alt-text="Create bot in SDK"::: + +You're now ready to build your bot with the Bot Framework SDK. + +> [!TIP] +> When Azure creates a new single-tenant or multi-tenant Azure Bot resource with a new app ID, it also generates a _password_. + +[!INCLUDE [app ID and password](../authentication/azure-bot-appid-password.md)] + +### To update your app service + +If you have an existing App Service resource (web app) for your bot and your bot is a _user-assigned managed identity_ application, you may need to update your bot's app service: + +1. Go to the App Service blade for your bot's web app. +1. Under **Settings**, select **Identity**. +1. On the **Identity** blade, select the **User assigned** tab and **Add** (+). +1. On the **Add user assigned managed identity** blade: + 1. Select your subscription. + 1. For **User assigned managed identities**, select the managed identity for your bot. If the managed identity was auto-generated for you, it will have the same name as your bot. + 1. Select **Add** to use this identity for your bot. + + :::image type="content" source="../../media/how-to-create-single-tenant-bot/app-service-managed-identity.png" alt-text="The App Service Identity blade with the managed identity for the bot selected."::: + +### To get your app or tenant ID + +To get your bot's app or tenant ID: + +1. Go to the Azure Bot resource blade for your bot. +1. Go to the bot's **Configuration** blade. + From this blade, you can copy the bot's **Microsoft App ID** or **App Tenant ID**. + +### To generate a new password + +Single-tenant and multi-tenant bots have an app secret or password that you need for some operations. +Azure AI Bot Service hides your bot secret. However, the owner of the bot's App Service resource can generate a new password: + +1. Go to the Azure Bot resource blade for your bot. +1. Go to the bot's **Configuration** blade. +1. Select **Manage**, next to **Microsoft App ID**, to go to the **Certificates + secrets** blade for the app service. +1. Follow the instructions on the blade to create a new client secret and record the value in a safe place. diff --git a/articles/includes/azure-bot-resource/identity-app-type-support.md b/articles/includes/azure-bot-resource/identity-app-type-support.md new file mode 100644 index 000000000..e93f8b639 --- /dev/null +++ b/articles/includes/azure-bot-resource/identity-app-type-support.md @@ -0,0 +1,34 @@ +--- +description: Note about product support for different identity management types in Azure Bot applications. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +Your bot's identity can be managed in Azure in several ways: + +- As a **user-assigned managed identity**, so you don’t need to manage credentials manually. +- As a **single-tenant** app. +- As a **multi-tenant** app. + +> [!NOTE] +> +> - Support for **user-assigned managed identity** and **single-tenant** app types is available in the Bot Framework SDK for C#, JavaScript, and Python. +> - These app types are **not supported** in other SDK languages, Bot Framework Composer, Bot Framework Emulator, or Dev Tunnels. +> [!IMPORTANT] +> +> - **Multi-tenant bot creation will be deprecated after July 31, 2025.** +> - Existing multi-tenant bots will continue to function, but new multi-tenant bot creation will no longer be supported after that date. +> - To ensure continued support, use **single-tenant** or **user-assigned managed identity** going forward. + +### Supported App Types + +| App Type | Supported In | +|--------------------------------------|-------------------------------------------------------------------------------------------------------------------| +| User-assigned managed identity | Azure AI Bot Service; C#, JavaScript, and Python SDKs | +| Single-tenant | Azure AI Bot Service; C#, JavaScript, and Python SDKs | +| Multi-tenant _(Deprecated – ends July 31, 2025)_ | Azure AI Bot Service; all Bot Framework SDK languages; Composer; Emulator; Dev Tunnels | diff --git a/articles/includes/azure-docs/apps-and-resources.md b/articles/includes/azure-docs/apps-and-resources.md new file mode 100644 index 000000000..3e780f5b3 --- /dev/null +++ b/articles/includes/azure-docs/apps-and-resources.md @@ -0,0 +1,25 @@ +--- +description: Provides links to Azure documentation for concepts and resources used in support of bot hosting. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +See these articles for more information about Azure applications and resources that are used to host a bot. + +| Subject | Article | +|:-|:-| +| Azure CLI | [What is the Azure CLI?](/cli/azure/what-is-azure-cli) | +| Azure subscription management | [How to manage Azure subscriptions with the Azure CLI](/cli/azure/manage-azure-subscriptions-azure-cli) | +| Azure regions | [Regions and availability zones](/azure/availability-zones/az-overview) | +| Resource groups and resource management | [Manage Azure resources](/azure/azure-resource-manager/management/) | +| Managed identities | [What are managed identities for Azure resources?](/azure/active-directory/managed-identities-azure-resources/overview) | +| Single-tenant and multi-tenant apps | [Tenancy in Microsoft Entra ID](/azure/active-directory/develop/single-and-multi-tenant-apps) | +| Web applications | [App Service](/azure/app-service/) | +| Compute resources for web applications | [App Service plans](/azure/app-service/azure-web-sites-web-hosting-plans-in-depth-overview) | +| Azure Resource Manager templates (ARM templates) | [What are ARM templates?](/azure/azure-resource-manager/templates/overview) and [How to use Azure Resource Manager (ARM) deployment templates with Azure CLI](/azure/azure-resource-manager/templates/deploy-cli) | +| Azure billing | [Billing and cost management](/azure/cost-management-billing/) | diff --git a/articles/includes/bot-resource-type-tip.md b/articles/includes/bot-resource-type-tip.md new file mode 100644 index 000000000..2501aec31 --- /dev/null +++ b/articles/includes/bot-resource-type-tip.md @@ -0,0 +1,14 @@ +--- +description: Common alert about the Web App Bot and Bot Channels Registration deprecation. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +> [!TIP] +> New _Web App Bot_ and _Bot Channels Registration_ resources can't be created; however, any such existing resources that are configured and deployed will continue to work. +> Bots created from a VSIX or Yeoman template from SDK version 4.14.1.2 or later contain ARM templates that will generate an Azure Bot resource. diff --git a/articles/includes/code/azure-bot-debug.js b/articles/includes/code/azure-bot-debug.js deleted file mode 100644 index 83889366b..000000000 --- a/articles/includes/code/azure-bot-debug.js +++ /dev/null @@ -1,5 +0,0 @@ -// -"env": { - "NODE\_ENV": "development" -} -// diff --git a/articles/includes/code/azure-bot-service-serverless-template-basic.cs b/articles/includes/code/azure-bot-service-serverless-template-basic.cs deleted file mode 100644 index 835893796..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-basic.cs +++ /dev/null @@ -1,139 +0,0 @@ -// -// Authenticates the incoming request. If the request is authentic, it adds the activity.ServiceUrl to -// MicrosoftAppCredentials.TrustedHostNames. Otherwise, it returns Unauthorized. - -if (!await botAuthenticator.Value.TryAuthenticateAsync(req, new [] {activity}, CancellationToken.None)) -{ - return BotAuthentication.GenerateUnauthorizedResponse(req); -} -// - - - -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Message: - // Processes the user’s message. - break; - case ActivityTypes.ConversationUpdate: - // Welcomes the users to the conversation. - break; - case ActivityTypes.ContactRelationUpdate: - case ActivityTypes.Typing: - case ActivityTypes.DeleteUserData: - default: - log.Error($"Unknown activity type ignored: {activity.GetActivityType()}"); - break; -} -// - - - -// -case ActivityTypes.ConversationUpdate: - IConversationUpdateActivity update = activity; - using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) - { - var client = scope.Resolve(); - if (update.MembersAdded.Any()) - { - var reply = activity.CreateReply(); - var newMembers = update.MembersAdded?.Where(t => t.Id != activity.Recipient.Id); - foreach (var newMember in newMembers) - { - reply.Text = "Welcome"; - if (!string.IsNullOrEmpty(newMember.Name)) - { - reply.Text += $" {newMember.Name}"; - } - reply.Text += "!"; - await client.Conversations.ReplyToActivityAsync(reply); - } - } - } - break; -// - - - -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Message: - await Conversation.SendAsync(activity, () => new EchoDialog()); - break; - ... -} -// - - - -// -[Serializable] -public class EchoDialog : IDialog -{ - protected int count = 1; - - public Task StartAsync(IDialogContext context) - { - try - { - context.Wait(MessageReceivedAsync); - } - catch (OperationCanceledException error) - { - return Task.FromCanceled(error.CancellationToken); - } - catch (Exception error) - { - return Task.FromException(error); - } - - return Task.CompletedTask; - } - ... -} -// - - - -// -public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) -{ - var message = await argument; - if (message.Text == "reset") - { - PromptDialog.Confirm( - context, - AfterResetAsync, - "Are you sure you want to reset the count?", - "Didn't get that!", - promptStyle: PromptStyle.Auto); - } - else - { - await context.PostAsync($"{this.count++}: You said {message.Text}"); - context.Wait(MessageReceivedAsync); - } -} -// - - - -// -public async Task AfterResetAsync(IDialogContext context, IAwaitable argument) -{ - var confirm = await argument; - if (confirm) - { - this.count = 1; - await context.PostAsync("Reset count."); - } - else - { - await context.PostAsync("Did not reset count."); - } - context.Wait(MessageReceivedAsync); -} -// diff --git a/articles/includes/code/azure-bot-service-serverless-template-form.cs b/articles/includes/code/azure-bot-service-serverless-template-form.cs deleted file mode 100644 index 4cc28e59b..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-form.cs +++ /dev/null @@ -1,91 +0,0 @@ -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Message: - await Conversation.SendAsync(activity, () => new MainDialog()); - break; - ... -} -// - - - -// -public class MainDialog : IDialog -{ - public MainDialog() - { - } - - public Task StartAsync(IDialogContext context) - { - context.Wait(MessageReceivedAsync); - return Task.CompletedTask; - } - ... -} -// - - - -// -public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) -{ - var message = await argument; - context.Call(BasicForm.BuildFormDialog(FormOptions.PromptInStart), FormComplete); -} - -private async Task FormComplete(IDialogContext context, IAwaitable result) -{ - try - { - var form = await result; - if (form != null) - { - await context.PostAsync("Thanks for completing the form! Just type anything to restart it."); - } - else - { - await context.PostAsync("Form returned empty response! Type anything to restart it."); - } - } - catch (OperationCanceledException) - { - await context.PostAsync("You canceled the form! Type anything to restart it."); - } - - context.Wait(MessageReceivedAsync); -} -// - - - -// -public enum CarOptions { Convertible=1, SUV, EV }; -public enum ColorOptions { Red=1, White, Blue }; - -[Serializable] -public class BasicForm -{ - [Prompt("Hi! What is your {&}?")] - public string Name { get; set; } - - [Prompt("Please select your favorite car type {||}")] - public CarOptions Car { get; set; } - - [Prompt("Please select your favorite {&} {||}")] - public ColorOptions Color { get; set; } - - public static IForm BuildForm() - { - // Builds an IForm based on BasicForm - return new FormBuilder().Build(); - } - - public static IFormDialog BuildFormDialog(FormOptions options = FormOptions.PromptInStart) - { - // Generate a new FormDialog based on IForm - return FormDialog.FromForm(BuildForm, options); - } -} -// diff --git a/articles/includes/code/azure-bot-service-serverless-template-language-understanding.cs b/articles/includes/code/azure-bot-service-serverless-template-language-understanding.cs deleted file mode 100644 index dc0544dae..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-language-understanding.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Message: - await Conversation.SendAsync(activity, () => new BasicLuisDialog()); - break; - ... -} -// - - -// -[Serializable] -public class BasicLuisDialog : LuisDialog -{ - public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(Utils.GetAppSetting("LuisAppId"), Utils.GetAppSetting("LuisAPIKey")))) - { - } - - [LuisIntent("None")] - public async Task NoneIntent(IDialogContext context, LuisResult result) - { - await context.PostAsync($"You have reached the none intent. You said: {result.Query}"); // - context.Wait(MessageReceived); - } - - [LuisIntent("MyIntent")] - public async Task MyIntent(IDialogContext context, LuisResult result) - { - await context.PostAsync($"You have reached the MyIntent intent. You said: {result.Query}"); // - context.Wait(MessageReceived); - } -} -// \ No newline at end of file diff --git a/articles/includes/code/azure-bot-service-serverless-template-proactive.cs b/articles/includes/code/azure-bot-service-serverless-template-proactive.cs deleted file mode 100644 index 0c7f52cc7..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-proactive.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) -{ - var message = await argument; - - // Create a queue Message - var queueMessage = new Message - { - RelatesTo = context.Activity.ToConversationReference(), - Text = message.Text - }; - - // Write the queue Message to the queue - // AddMessageToQueue() is a utility method you can find in the template - await AddMessageToQueue(JsonConvert.SerializeObject(queueMessage)); - await context.PostAsync($"{this.count++}: You said {queueMessage.Text}. Message added to the queue."); - context.Wait(MessageReceivedAsync); -} -// - - - -// -using System; -using System.Net; -using System.Net.Http; -using Microsoft.Azure.WebJobs.Host; - -public class BotMessage -{ - public string Source { get; set; } - public string Message { get; set; } -} - -public static HttpResponseMessage Run(string myQueueItem, out BotMessage message, TraceWriter log) -{ - message = new BotMessage - { - Source = "Azure Functions (C#)!", - Message = myQueueItem - }; - - return new HttpResponseMessage(HttpStatusCode.OK); -} -// - - - -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Event: - // handle proactive Message from function - IEventActivity triggerEvent = activity; - var message = JsonConvert.DeserializeObject(((JObject) triggerEvent.Value).GetValue("Message").ToString()); - var messageactivity = (Activity)message.RelatesTo.GetPostToBotMessage(); - - client = new ConnectorClient(new Uri(messageactivity.ServiceUrl)); - var triggerReply = messageactivity.CreateReply(); - triggerReply.Text = $"This is coming back from the trigger! {message.Text}"; - await client.Conversations.ReplyToActivityAsync(triggerReply); - break; - default: - break; -} -// \ No newline at end of file diff --git a/articles/includes/code/azure-bot-service-serverless-template-proactive.js b/articles/includes/code/azure-bot-service-serverless-template-proactive.js deleted file mode 100644 index 6b65d4aef..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-proactive.js +++ /dev/null @@ -1,49 +0,0 @@ -// -bot.dialog('/', function (session) { - var queuedMessage = { address: session.message.address, text: session.message.text }; - session.sendTyping(); - var queueSvc = azure.createQueueService(process.env.AzureWebJobsStorage); - queueSvc.createQueueIfNotExists('bot-queue', function(err, result, response){ - if(!err){ - var queueMessageBuffer = new Buffer(JSON.stringify(queuedMessage)).toString('base64'); - queueSvc.createMessage('bot-queue', queueMessageBuffer, function(err, result, response){ - if(!err){ - session.send('Your message (\'' + session.message.text + '\') has been added to a queue, and it will be sent back to you via a Function'); - } else { - session.send('There was an error inserting your message into queue'); - } - }); - } else { - session.send('There was an error creating your queue'); - } - }); -}); -// - - - -// -module.exports = function (context, myQueueItem) { - context.log('Sending Bot message', myQueueItem); - - var message = { - 'text': myQueueItem.text, - 'address': myQueueItem.address - }; - - context.done(null, message); -} -// - - - -// -bot.on('trigger', function (message) { - // handle message from trigger function - var queuedMessage = message.value; - var reply = new builder.Message() - .address(queuedMessage.address) - .text('This is coming from the trigger: ' + queuedMessage.text); - bot.send(reply); -}); -// diff --git a/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.cs b/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.cs deleted file mode 100644 index f5253c38c..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -switch (activity.GetActivityType()) -{ - case ActivityTypes.Message: - await Conversation.SendAsync(activity, () => new BasicQnAMakerDialog()); - break; - ... -} -// - - - -// -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Threading; -using Microsoft.Bot.Connector; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.CognitiveServices.QnAMaker; - -[Serializable] -public class BasicQnAMakerDialog : QnAMakerDialog -{ - //Parameters to QnAMakerService are: - //Required: subscriptionKey, knowledgebaseId, - //Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0] - public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(Utils.GetAppSetting("QnASubscriptionKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5))) - {} -} -// - - - - \ No newline at end of file diff --git a/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.js b/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.js deleted file mode 100644 index 324ade8eb..000000000 --- a/articles/includes/code/azure-bot-service-serverless-template-question-and-answer.js +++ /dev/null @@ -1,17 +0,0 @@ -// -bot.dialog('/', BasicQnAMakerDialog); -// - - - -// -var recognizer = new cognitiveservices.QnAMakerRecognizer({ - knowledgeBaseId: 'set your kbid here', - subscriptionKey: 'set your subscription key here'}); - -var BasicQnAMakerDialog = new cognitiveservices.QnAMakerDialog({ - recognizers: [recognizer], - defaultMessage: 'No good match in FAQ.', - qnaThreshold: 0.5}); -// - diff --git a/articles/includes/code/bot-service-channel-connect-webchat-speech.js b/articles/includes/code/bot-service-channel-connect-webchat-speech.js deleted file mode 100644 index 448296c3e..000000000 --- a/articles/includes/code/bot-service-channel-connect-webchat-speech.js +++ /dev/null @@ -1,66 +0,0 @@ -// Code snippets for channel-connect-webchat-speech.md - -// -const speechOptions = { - speechRecognizer: new BotChat.Speech.BrowserSpeechRecognizer(), - speechSynthesizer: new BotChat.Speech.BrowserSpeechSynthesizer() -}; -// - -// -const speechOptions = { - speechRecognizer: new CognitiveServices.SpeechRecognizer({ subscriptionKey: 'YOUR_COGNITIVE_SPEECH_API_KEY' }), - speechSynthesizer: new CognitiveServices.SpeechSynthesizer({ - gender: CognitiveServices.SynthesisGender.Female, - subscriptionKey: 'YOUR_COGNITIVE_SPEECH_API_KEY', - voiceName: 'Microsoft Server Speech Text to Speech Voice (en-US, JessaRUS)' - }) - }; -// - -// -function getToken() { -// This call would be to your backend, or to retrieve a token that was served as part of the original page. -return fetch( - 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken', - { - headers: { - 'Ocp-Apim-Subscription-Key': 'YOUR_COGNITIVE_SPEECH_API_KEY' - }, - method: 'POST' - } -).then(res => res.text()); -} - -const speechOptions = { - speechRecognizer: new CognitiveServices.SpeechRecognizer({ - fetchCallback: (authFetchEventId) => getToken(), - fetchOnExpiryCallback: (authFetchEventId) => getToken() - }), - speechSynthesizer: new BotChat.Speech.BrowserSpeechSynthesizer() -}; -// - -// -const speechOptions = { - speechRecognizer: new YourOwnSpeechRecognizer(), - speechSynthesizer: new YourOwnSpeechSynthesizer() - }; -// - -// -BotChat.App({ - bot: bot, - locale: params['locale'], - resize: 'detect', - // sendTyping: true, // defaults to false. set to true to send 'typing' activities to bot (and other users) when user is typing - speechOptions: speechOptions, - user: user, - directLine: { - domain: params['domain'], - secret: params['s'], - token: params['t'], - webSocket: params['webSocket'] && params['webSocket'] === 'true' // defaults to true - } - }, document.getElementById('BotChatGoesHere')); -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-add-attachments.cs b/articles/includes/code/dotnet-add-attachments.cs deleted file mode 100644 index b47a1eda1..000000000 --- a/articles/includes/code/dotnet-add-attachments.cs +++ /dev/null @@ -1,254 +0,0 @@ -// -replyMessage.Attachments.Add(new Attachment() -{ - ContentUrl = "https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png", - ContentType = "image/png", - Name = "Bender_Rodriguez.png" -}); -// - - - -// -Activity replyToConversation = message.CreateReply("Should go to conversation, in carousel format"); -replyToConversation.AttachmentLayout = AttachmentLayoutTypes.Carousel; -replyToConversation.Attachments = new List(); - -Dictionary cardContentList = new Dictionary(); -cardContentList.Add("PigLatin", "https://"); -cardContentList.Add("Pork Shoulder", "https://"); -cardContentList.Add("Bacon", "https://"); - -foreach(KeyValuePair cardContent in cardContentList) -{ - List cardImages = new List(); - cardImages.Add(new CardImage(url:cardContent.Value )); - - List cardButtons = new List(); - - CardAction plButton = new CardAction() - { - Value = $"https://en.wikipedia.org/wiki/{cardContent.Key}", - Type = "openUrl", - Title = "WikiPedia Page" - }; - - cardButtons.Add(plButton); - - HeroCard plCard = new HeroCard() - { - Title = $"I'm a hero card about {cardContent.Key}", - Subtitle = $"{cardContent.Key} Wikipedia Page", - Images = cardImages, - Buttons = cardButtons - }; - - Attachment plAttachment = plCard.ToAttachment(); - replyToConversation.Attachments.Add(plAttachment); -} - -var reply = await connector.Conversations.SendToConversationAsync(replyToConversation); -// - - - -// -Activity replyToConversation = message.CreateReply("Should go to conversation, in list format"); -replyToConversation.AttachmentLayout = AttachmentLayoutTypes.List; -replyToConversation.Attachments = new List(); - -Dictionary cardContentList = new Dictionary(); -cardContentList.Add("PigLatin", "https://"); -cardContentList.Add("Pork Shoulder", "https://"); - -foreach(KeyValuePair cardContent in cardContentList) -{ - List cardImages = new List(); - cardImages.Add(new CardImage(url:cardContent.Value )); - - List cardButtons = new List(); - - CardAction plButton = new CardAction() - { - Value = $"https://en.wikipedia.org/wiki/{cardContent.Key}", - Type = "openUrl", - Title = "WikiPedia Page" - }; - - cardButtons.Add(plButton); - - ThumbnailCard plCard = new ThumbnailCard() - { - Title = $"I'm a thumbnail card about {cardContent.Key}", - Subtitle = $"{cardContent.Key} Wikipedia Page", - Images = cardImages, - Buttons = cardButtons - }; - - Attachment plAttachment = plCard.ToAttachment(); - replyToConversation.Attachments.Add(plAttachment); -} - -var reply = await connector.Conversations.SendToConversationAsync(replyToConversation); -// - - - -// -Activity replyToConversation = message.CreateReply("Should go to conversation"); -replyToConversation.Attachments = new List(); - -List cardImages = new List(); -cardImages.Add(new CardImage(url: "https://" )); - -List cardButtons = new List(); - -CardAction plButton = new CardAction() -{ - Value = $"https://en.wikipedia.org/wiki/PigLatin", - Type = "openUrl", - Title = "WikiPedia Page" -}; - -cardButtons.Add(plButton); - -ReceiptItem lineItem1 = new ReceiptItem() -{ - Title = "Pork Shoulder", - Subtitle = "8 lbs", - Text = null, - Image = new CardImage(url: "https://"), - Price = "16.25", - Quantity = "1", - Tap = null -}; - -ReceiptItem lineItem2 = new ReceiptItem() -{ -Title = "Bacon", -Subtitle = "5 lbs", -Text = null, -Image = new CardImage(url: "https://"), -Price = "34.50", -Quantity = "2", -Tap = null -}; - -List receiptList = new List(); -receiptList.Add(lineItem1); -receiptList.Add(lineItem2); - -ReceiptCard plCard = new ReceiptCard() -{ - Title = "I'm a receipt card, isn't this bacon expensive?", - Buttons = cardButtons, - Items = receiptList, - Total = "112.77", - Tax = "27.52" -}; - -Attachment plAttachment = plCard.ToAttachment(); -replyToConversation.Attachments.Add(plAttachment); - -var reply = await connector.Conversations.SendToConversationAsync(replyToConversation); -// - - - -// -Activity replyToConversation = message.CreateReply("Should go to conversation"); -replyToConversation.Attachments = new List(); - -List cardButtons = new List(); - -CardAction plButton = new CardAction() -{ - Value = $"https:// - - - -// -Activity replyToConversation = message.CreateReply("Should go to conversation"); -replyToConversation.Attachments = new List(); - -AdaptiveCard card = new AdaptiveCard(); - -// Specify speech for the card. -card.Speak = "Your meeting about \"Adaptive Card design session\" is starting at 12:30pmDo you want to snooze or do you want to send a late notification to the attendees?"; - -// Add text to the card. -card.Body.Add(new TextBlock() -{ - Text = "Adaptive Card design session", - Size = TextSize.Large, - Weight = TextWeight.Bolder -}); - -// Add text to the card. -card.Body.Add(new TextBlock() -{ - Text = "Conf Room 112/3377 (10)" -}); - -// Add text to the card. -card.Body.Add(new TextBlock() -{ - Text = "12:30 PM - 1:30 PM" -}); - -// Add list of choices to the card. -card.Body.Add(new ChoiceSet() -{ - Id = "snooze", - Style = ChoiceInputStyle.Compact, - Choices = new List() - { - new Choice() { Title = "5 minutes", Value = "5", IsSelected = true }, - new Choice() { Title = "15 minutes", Value = "15" }, - new Choice() { Title = "30 minutes", Value = "30" } - } -}); - -// Add buttons to the card. -card.Actions.Add(new OpenUrlAction() -{ - Url = "http://foo.com", - Title = "Snooze" -}); - -card.Actions.Add(new OpenUrlAction() -{ - Url = "http://foo.com", - Title = "I'll be late" -}); - -card.Actions.Add(new OpenUrlAction() -{ - Url = "http://foo.com", - Title = "Dismiss" -}); - -// Create the attachment. -Attachment attachment = new Attachment() -{ - ContentType = AdaptiveCard.ContentType, - Content = card -}; - -replyToConversation.Attachments.Add(attachment); - -var reply = await connector.Conversations.SendToConversationAsync(replyToConversation); -// diff --git a/articles/includes/code/dotnet-add-suggested-actions.cs b/articles/includes/code/dotnet-add-suggested-actions.cs deleted file mode 100644 index ce10a58b8..000000000 --- a/articles/includes/code/dotnet-add-suggested-actions.cs +++ /dev/null @@ -1,15 +0,0 @@ -// -var reply = activity.CreateReply("I have colors in mind, but need your help to choose the best one."); -reply.Type = ActivityTypes.Message; -reply.TextFormat = TextFormatTypes.Plain; - -reply.SuggestedActions = new SuggestedActions() -{ - Actions = new List() - { - new CardAction(){ Title = "Blue", Type=ActionTypes.ImBack, Value="Blue" }, - new CardAction(){ Title = "Red", Type=ActionTypes.ImBack, Value="Red" }, - new CardAction(){ Title = "Green", Type=ActionTypes.ImBack, Value="Green" } - } -}; -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-create-messages.cs b/articles/includes/code/dotnet-create-messages.cs deleted file mode 100644 index e7a4238a4..000000000 --- a/articles/includes/code/dotnet-create-messages.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -IMessageActivity message = Activity.CreateMessageActivity(); -message.Text = "Hello!"; -message.TextFormat = "plain"; -message.Locale = "en-Us"; -// - -// -var entity = new Entity(); -entity.SetAs(new Mention() -{ - Text = "@johndoe", - Mentioned = new ChannelAccount() - { - Name = "John Doe", - Id = "UV341235" - } -}); -entities.Add(entity); -// - -// -var entity = new Entity(); -entity.SetAs(new Place() -{ - Geo = new GeoCoordinates() - { - Latitude = 32.4141, - Longitude = 43.1123123, - } -}); -entities.Add(entity); -// - -// -if (entity.Type == "Place") -{ - dynamic place = entity.Properties; - if (place.geo.latitude > 34) - // do something -} -// - -// -if (entity.Type == "Place") -{ - Place place = entity.GetAs(); - GeoCoordinates geo = place.Geo.ToObject(); - if (geo.Latitude > 34) - // do something -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-dialogs.cs b/articles/includes/code/dotnet-dialogs.cs deleted file mode 100644 index 96d9272d2..000000000 --- a/articles/includes/code/dotnet-dialogs.cs +++ /dev/null @@ -1,163 +0,0 @@ -// -using Microsoft.Bot.Builder.Dialogs; -// - - -// -[Serializable] -public class EchoDialog : IDialog -{ - public async Task StartAsync(IDialogContext context) - { - context.Wait(MessageReceivedAsync); - } - - public async Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) - { - var message = await argument; - await context.PostAsync("You said: " + message.Text); - context.Wait(MessageReceivedAsync); - } -} -// - - -// -public virtual async Task Post([FromBody] Activity activity) -{ - // Check if activity is of type message - if (activity != null && activity.GetActivityType() == ActivityTypes.Message) - { - await Conversation.SendAsync(activity, () => new EchoDialog()); - } - else - { - HandleSystemMessage(activity); - } - return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted); -} -// - - -// -[Serializable] -public class EchoDialog : IDialog -{ - protected int count = 1; - - public async Task StartAsync(IDialogContext context) - { - context.Wait(MessageReceivedAsync); - } - - public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) - { - var message = await argument; - if (message.Text == "reset") - { - PromptDialog.Confirm( - context, - AfterResetAsync, - "Are you sure you want to reset the count?", - "Didn't get that!", - promptStyle: PromptStyle.None); - } - else - { - await context.PostAsync($"{this.count++}: You said {message.Text}"); - context.Wait(MessageReceivedAsync); - } - } - - public async Task AfterResetAsync(IDialogContext context, IAwaitable argument) - { - var confirm = await argument; - if (confirm) - { - this.count = 1; - await context.PostAsync("Reset count."); - } - else - { - await context.PostAsync("Did not reset count."); - } - context.Wait(MessageReceivedAsync); - } -} -// - - -// -var builder = new ContainerBuilder(); -builder.RegisterModule(new DialogModule()); -builder.RegisterModule(new ReflectionSurrogateModule()); -// - - -// -var query = from x in new PromptDialog.PromptString(Prompt, Prompt, attempts: 1) - let w = new string(x.Reverse().ToArray()) - select w; -// - - -// -var query = from x in new PromptDialog.PromptString("p1", "p1", 1) - from y in new PromptDialog.PromptString("p2", "p2", 1) - select string.Join(" ", x, y); -// - - -// -query = query.PostToUser(); -// - - -// -var logic = - toBot - .Switch - ( - new RegexCase(new Regex("^hello"), (context, text) => - { - return "world!"; - }), - new Case((txt) => txt == "world", (context, text) => - { - return "!"; - }), - new DefaultCase((context, text) => - { - return text; - } - ) -); -// - - -// -var joke = Chain - .PostToChain() - .Select(m => m.Text) - .Switch - ( - Chain.Case - ( - new Regex("^chicken"), - (context, text) => - Chain - .Return("why did the chicken cross the road?") - .PostToUser() - .WaitToBot() - .Select(ignoreUser => "to get to the other side") - ), - Chain.Default>( - (context, text) => - Chain - .Return("why don't you like chicken jokes?") - ) - ) - .Unwrap() - .PostToUser(). - Loop(); -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-formflow-advanced.cs b/articles/includes/code/dotnet-formflow-advanced.cs deleted file mode 100644 index 2c86a33f3..000000000 --- a/articles/includes/code/dotnet-formflow-advanced.cs +++ /dev/null @@ -1,88 +0,0 @@ -// -public static IForm BuildForm() -{ - ... - return new FormBuilder() - .Message("Welcome to the sandwich order bot!") - .Field(nameof(Sandwich)) - .Field(nameof(Length)) - .Field(nameof(Bread)) - .Field(nameof(Cheese)) - .Field(nameof(Toppings), - validate: async (state, value) => - { - var values = ((List)value).OfType(); - var result = new ValidateResult { IsValid = true, Value = values }; - if (values != null && values.Contains(ToppingOptions.Everything)) - { - result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions)) - where topping != ToppingOptions.Everything && !values.Contains(topping) - select topping).ToList(); - } - return result; - }) - .Message("For sandwich toppings you have selected {Toppings}.") - ... - .Build(); -} -// - - -// -public enum ToppingOptions -{ - // This starts at 1 because 0 is the "no value" value - [Terms("except", "but", "not", "no", "all", "everything")] - Everything = 1, - ... -} -// - - -// -[Prompt("What kind of {&} would you like? {||}")] -public SandwichOptions? Sandwich; -// - - -// -[Prompt("What kind of {&} would you like? {||}", ChoiceFormat="{1}")] -public SandwichOptions? Sandwich; -// - - -// -[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}", ChoiceStyle = ChoiceStyleOptions.PerLine)] -public class SandwichOrder -// - - -// -[Template(TemplateUsage.NotUnderstood, "I do not understand \"{0}\".", "Try again, I don't get \"{0}\".")] -[Template(TemplateUsage.EnumSelectOne, "What kind of {&} would you like on your sandwich? {||}")] -public class SandwichOrder -// - - -// -[Optional] -public CheeseOptions? Cheese; -// - - -// -[Terms(@"rotis\w* style chicken", MaxPhrase = 3)] -RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna,... -// - - -// -[Numeric(1, 5)] -public double? Rating; -// - - -// -[Pattern(@"(\d)?\s*\d{3}(-|\s*)\d{4}")] -public string PhoneNumber; -// diff --git a/articles/includes/code/dotnet-formflow-formbuilder.cs b/articles/includes/code/dotnet-formflow-formbuilder.cs deleted file mode 100644 index df2b28d65..000000000 --- a/articles/includes/code/dotnet-formflow-formbuilder.cs +++ /dev/null @@ -1,108 +0,0 @@ -// -[Optional] -[Template(TemplateUsage.NoPreference, "None")] -public string Specials; -// - - -// -.Field(new FieldReflector(nameof(Specials)) - .SetType(null) - .SetActive((state) => state.Length == LengthOptions.FootLong) - .SetDefine(async (state, field) => - { - field - .AddDescription("cookie", "Free cookie") - .AddTerms("cookie", "cookie", "free cookie") - .AddDescription("drink", "Free large drink") - .AddTerms("drink", "drink", "free drink"); - return true; - })) -// - - -// -.Confirm(async (state) => -{ - var cost = 0.0; - switch (state.Length) - { - case LengthOptions.SixInch: cost = 5.0; break; - case LengthOptions.FootLong: cost = 6.50; break; - } - return new PromptAttribute($"Total for your sandwich is {cost:C2} is that ok?"); -}) -// - - -// -public static IForm BuildForm() -{ - OnCompletionAsyncDelegate processOrder = async (context, state) => - { - await context.PostAsync("We are currently processing your sandwich. We will message you the status."); - }; - - return new FormBuilder() - .Message("Welcome to the sandwich order bot!") - .Field(nameof(Sandwich)) - .Field(nameof(Length)) - .Field(nameof(Bread)) - .Field(nameof(Cheese)) - .Field(nameof(Toppings), - validate: async (state, value) => - { - var values = ((List)value).OfType(); - var result = new ValidateResult { IsValid = true, Value = values }; - if (values != null && values.Contains(ToppingOptions.Everything)) - { - result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions)) - where topping != ToppingOptions.Everything && !values.Contains(topping) - select topping).ToList(); - } - return result; - }) - .Message("For sandwich toppings you have selected {Toppings}.") - .Field(nameof(SandwichOrder.Sauces)) - .Field(new FieldReflector(nameof(Specials)) - .SetType(null) - .SetActive((state) => state.Length == LengthOptions.FootLong) - .SetDefine(async (state, field) => - { - field - .AddDescription("cookie", "Free cookie") - .AddTerms("cookie", "cookie", "free cookie") - .AddDescription("drink", "Free large drink") - .AddTerms("drink", "drink", "free drink"); - return true; - })) - .Confirm(async (state) => - { - var cost = 0.0; - switch (state.Length) - { - case LengthOptions.SixInch: cost = 5.0; break; - case LengthOptions.FootLong: cost = 6.50; break; - } - return new PromptAttribute($"Total for your sandwich is {cost:C2} is that ok?"); - }) - .Field(nameof(SandwichOrder.DeliveryAddress), - validate: async (state, response) => - { - var result = new ValidateResult { IsValid = true, Value = response }; - var address = (response as string).Trim(); - if (address.Length > 0 && (address[0] < '0' || address[0] > '9')) - { - result.Feedback = "Address must start with a number."; - result.IsValid = false; - } - return result; - }) - .Field(nameof(SandwichOrder.DeliveryTime), "What time do you want your sandwich delivered? {||}") - .Confirm("Do you want to order your {Length} {Sandwich} on {Bread} {&Bread} with {[{Cheese} {Toppings} {Sauces}]} to be sent to {DeliveryAddress} {?at {DeliveryTime:t}}?") - .AddRemainingFields() - .Message("Thanks for ordering a sandwich!") - .OnCompletion(processOrder) - .Build(); -} -// diff --git a/articles/includes/code/dotnet-formflow-json-schema.cs b/articles/includes/code/dotnet-formflow-json-schema.cs deleted file mode 100644 index 924b1f1f2..000000000 --- a/articles/includes/code/dotnet-formflow-json-schema.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -public static IForm BuildJsonForm() -{ - using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Bot.Sample.AnnotatedSandwichBot.AnnotatedSandwich.json")) - { - var schema = JObject.Parse(new StreamReader(stream).ReadToEnd()); - return new FormBuilderJson(schema) - .AddRemainingFields() - .Build(); - } - ... -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-formflow-localize.cs b/articles/includes/code/dotnet-formflow-localize.cs deleted file mode 100644 index 30c9780fa..000000000 --- a/articles/includes/code/dotnet-formflow-localize.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -public static IForm BuildLocalizedForm() -{ - var culture = Thread.CurrentThread.CurrentUICulture; - IForm form; - if (!_forms.TryGetValue(culture, out form)) - { - OnCompletionAsyncDelegate processOrder = async (context, state) => - { - await context.PostAsync(DynamicSandwich.Processing); - }; - // FormBuilder uses the thread culture to automatically switch framework strings and static strings. - // Dynamically defined fields must do their own localization. - var builder = new FormBuilder() - .Message("Welcome to the sandwich order bot!") - .Field(nameof(Sandwich)) - .Field(nameof(Length)) - .Field(nameof(Bread)) - .Field(nameof(Cheese)) - .Field(nameof(Toppings), - validate: async (state, value) => - { - var values = ((List)value).OfType(); - var result = new ValidateResult { IsValid = true, Value = values }; - if (values != null && values.Contains(ToppingOptions.Everything)) - { - result.Value = (from ToppingOptions topping in Enum.GetValues(typeof(ToppingOptions)) - where topping != ToppingOptions.Everything && !values.Contains(topping) - select topping).ToList(); - } - return result; - }) - .Message("For sandwich toppings you have selected {Toppings}.") - .Field(nameof(SandwichOrder.Sauces)) - .Field(new FieldReflector(nameof(Specials)) - .SetType(null) - .SetActive((state) => state.Length == LengthOptions.FootLong) - .SetDefine(async (state, field) => - { - field - .AddDescription("cookie", DynamicSandwich.FreeCookie) - .AddTerms("cookie", Language.GenerateTerms(DynamicSandwich.FreeCookie, 2)) - .AddDescription("drink", DynamicSandwich.FreeDrink) - .AddTerms("drink", Language.GenerateTerms(DynamicSandwich.FreeDrink, 2)); - return true; - })) - .Confirm(async (state) => - { - var cost = 0.0; - switch (state.Length) - { - case LengthOptions.SixInch: cost = 5.0; break; - case LengthOptions.FootLong: cost = 6.50; break; - } - return new PromptAttribute(string.Format(DynamicSandwich.Cost, cost) + "{||}"); - }) - .Field(nameof(SandwichOrder.DeliveryAddress), - validate: async (state, response) => - { - var result = new ValidateResult { IsValid = true, Value = response }; - var address = (response as string).Trim(); - if (address.Length > 0 && address[0] < '0' || address[0] > '9') - { - result.Feedback = DynamicSandwich.BadAddress; - result.IsValid = false; - } - return result; - }) - .Field(nameof(SandwichOrder.DeliveryTime), "What time do you want your sandwich delivered? {||}") - .Confirm("Do you want to order your {Length} {Sandwich} on {Bread} {&Bread} with {[{Cheese} {Toppings} {Sauces}]} to be sent to {DeliveryAddress} {?at {DeliveryTime:t}}?") - .AddRemainingFields() - .Message("Thanks for ordering a sandwich!") - .OnCompletion(processOrder); - builder.Configuration.DefaultPrompt.ChoiceStyle = ChoiceStyleOptions.Auto; - form = builder.Build(); - _forms[culture] = form; - } - return form; -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-formflow-pattern-language.cs b/articles/includes/code/dotnet-formflow-pattern-language.cs deleted file mode 100644 index 22623d4eb..000000000 --- a/articles/includes/code/dotnet-formflow-pattern-language.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -[Prompt("What kind of {&} would you like? {||}")] -public SandwichOptions? Sandwich; -// - - -// -[Prompt("What kind of {&} would you like? {||}", ChoiceFormat="{1}")] -public SandwichOptions? Sandwich; -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-formflow.cs b/articles/includes/code/dotnet-formflow.cs deleted file mode 100644 index 625edc9bf..000000000 --- a/articles/includes/code/dotnet-formflow.cs +++ /dev/null @@ -1,120 +0,0 @@ -// -using Microsoft.Bot.Builder.FormFlow; -using System; -using System.Collections.Generic; - -// The SandwichOrder class represents the form that you want to complete -// using information that is collected from the user. -// It must be serializable so the bot can be stateless. -// The order of fields defines the default sequence in which the user is asked questions. - -// The enumerations define the valid options for each field in SandwichOrder, and the order -// of the values represents the sequence in which they are presented to the user in a conversation. - -namespace Microsoft.Bot.Sample.SimpleSandwichBot -{ - public enum SandwichOptions - { - BLT, BlackForestHam, BuffaloChicken, ChickenAndBaconRanchMelt, ColdCutCombo, MeatballMarinara, - OvenRoastedChicken, RoastBeef, RotisserieStyleChicken, SpicyItalian, SteakAndCheese, SweetOnionTeriyaki, Tuna, - TurkeyBreast, Veggie - }; - public enum LengthOptions { SixInch, FootLong }; - public enum BreadOptions { NineGrainWheat, NineGrainHoneyOat, Italian, ItalianHerbsAndCheese, Flatbread }; - public enum CheeseOptions { American, MontereyCheddar, Pepperjack }; - public enum ToppingOptions - { - Avocado, BananaPeppers, Cucumbers, GreenBellPeppers, Jalapenos, - Lettuce, Olives, Pickles, RedOnion, Spinach, Tomatoes - }; - public enum SauceOptions - { - ChipotleSouthwest, HoneyMustard, LightMayonnaise, RegularMayonnaise, - Mustard, Oil, Pepper, Ranch, SweetOnion, Vinegar - }; - - [Serializable] - public class SandwichOrder - { - public SandwichOptions? Sandwich; - public LengthOptions? Length; - public BreadOptions? Bread; - public CheeseOptions? Cheese; - public List Toppings; - public List Sauce; - - public static IForm BuildForm() - { - return new FormBuilder() - .Message("Welcome to the simple sandwich order bot!") - .Build(); - } - }; -} -// - - - - -// -internal static IDialog MakeRootDialog() -{ - return Chain.From(() => FormDialog.FromForm(SandwichOrder.BuildForm)); -} - -[ResponseType(typeof(void))] -public virtual async Task Post([FromBody] Activity activity) -{ - if (activity != null) - { - switch (activity.GetActivityType()) - { - case ActivityTypes.Message: - await Conversation.SendAsync(activity, MakeRootDialog); - break; - - case ActivityTypes.ConversationUpdate: - case ActivityTypes.ContactRelationUpdate: - case ActivityTypes.Typing: - case ActivityTypes.DeleteUserData: - default: - Trace.TraceError($"Unknown activity type ignored: {activity.GetActivityType()}"); - break; - } - } - ... -} -// - - - - -// -internal static IDialog MakeRootDialog() -{ - return Chain.From(() => FormDialog.FromForm(SandwichOrder.BuildLocalizedForm)) - .Do(async (context, order) => - { - try - { - var completed = await order; - // Actually process the sandwich order... - await context.PostAsync("Processed your order!"); - } - catch (FormCanceledException e) - { - string reply; - if (e.InnerException == null) - { - reply = $"You quit on {e.Last} -- maybe you can finish next time!"; - } - else - { - reply = "Sorry, I've had a short circuit. Please try again."; - } - await context.PostAsync(reply); - } - }); -} -// - diff --git a/articles/includes/code/dotnet-getstarted.cs b/articles/includes/code/dotnet-getstarted.cs deleted file mode 100644 index be1c892ce..000000000 --- a/articles/includes/code/dotnet-getstarted.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -[BotAuthentication] -public class MessagesController : ApiController -{ - /// - /// POST: api/Messages - /// Receive a message from a user and reply to it - /// - public async Task Post([FromBody]Activity activity) - { - if (activity.Type == ActivityTypes.Message) - { - await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); - } - else - { - HandleSystemMessage(activity); - } - var response = Request.CreateResponse(HttpStatusCode.OK); - return response; - } - ... -} -// - - -// -[Serializable] -public class RootDialog : IDialog -{ - public Task StartAsync(IDialogContext context) - { - context.Wait(MessageReceivedAsync); - return Task.CompletedTask; - } - - private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) - { - var activity = await result as Activity; - - // calculate something for us to return - int length = (activity.Text ?? string.Empty).Length; - - // return our reply to the user - await context.PostAsync($"You sent {activity.Text} which was {length} characters"); - - context.Wait(MessageReceivedAsync); - } -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-input-hints.cs b/articles/includes/code/dotnet-input-hints.cs deleted file mode 100644 index 72116debe..000000000 --- a/articles/includes/code/dotnet-input-hints.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Bot; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Core.Extensions; -using Microsoft.Bot.Schema; - -namespace InputHintsPublic -{ - public class EchoBot : IBot - { - /// - /// Every Conversation turn for our EchoBot will call this method. In here - /// the bot checks the Activty type to verify it's a message, bumps the - /// turn conversation 'Turn' count, and then echoes the users typing - /// back to them. - /// - /// Turn scoped context containing all the data needed - /// for processing this conversation turn. - public async Task OnTurn(ITurnContext context) - { - // This bot is only handling Messages - if (context.Activity.Type == ActivityTypes.Message) - { - { - // - var reply = MessageFactory.Text( - "This is the text that will be displayed.", - "This is the text that will be spoken.", - InputHints.AcceptingInput); - await context.SendActivity(reply).ConfigureAwait(false); - // - } - { - // - var reply = MessageFactory.Text( - "This is the text that will be displayed.", - "This is the text that will be spoken.", - InputHints.ExpectingInput); - await context.SendActivity(reply).ConfigureAwait(false); - // - } - { - // - var reply = MessageFactory.Text( - "This is the text that will be displayed.", - "This is the text that will be spoken.", - InputHints.IgnoringInput); - await context.SendActivity(reply).ConfigureAwait(false); - // - } - } - } - } -} \ No newline at end of file diff --git a/articles/includes/code/dotnet-luis-dialogs.cs b/articles/includes/code/dotnet-luis-dialogs.cs deleted file mode 100644 index 027f3f032..000000000 --- a/articles/includes/code/dotnet-luis-dialogs.cs +++ /dev/null @@ -1,606 +0,0 @@ -// -[LuisModel("c413b2ef-382c-45bd-8ff0-f76d60e2a821", "6d0966209c6e4f6b835ce34492f3e6d9", domain: "westus2.api.cognitive.microsoft.com")] -[Serializable] -public class SimpleNoteDialog : LuisDialog -{ - ... -} -// - -// - [LuisModel("", "YOUR_SUBSCRIPTION_KEY", domain: "westus.api.cognitive.microsoft.com")] - [Serializable] - public class SimpleNoteDialog : LuisDialog - { - // ... - } - -// - -// -[LuisModel("", "YOUR_SUBSCRIPTION_KEY", Log = false)] -[Serializable] -public class SimpleNoteDialog : LuisDialog -{ - // ... -} -// - -// -[LuisIntent("builtin.intent.alarm.turn_off_alarm")] -public async Task TurnOffAlarm(IDialogContext context, LuisResult result) -{ - if (TryFindAlarm(result, out this.turnOff)) - { - PromptDialog.Confirm(context, AfterConfirming_TurnOffAlarm, "Are you sure?", promptStyle: PromptStyle.None); - } - else - { - await context.PostAsync("did not find alarm"); - context.Wait(MessageReceived); - } -} -// - -// -[LuisIntent("Note.Delete")] -public async Task DeleteNote(IDialogContext context, LuisResult result) -{ - Note note; - if (TryFindNote(result, out note)) - { - this.noteByTitle.Remove(note.Title); - await context.PostAsync($"Note {note.Title} deleted"); - } - else - { - // Prompt the user for a note title - PromptDialog.Text(context, After_DeleteTitlePrompt, "What is the title of the note you want to delete?"); - } -} -// - -// - public bool TryFindNote(LuisResult result, out Note note) - { - note = null; - - string titleToFind; - - EntityRecommendation title; - if (result.TryFindEntity(Entity_Note_Title, out title)) - { - return this.noteByTitle.TryGetValue(title.Entity, out note); // TryGetValue returns false if no match is found. - } - else - { - return false; - } - } -// - -// -using System; -using System.Threading.Tasks; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Luis; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Bot.Builder.Luis.Models; - -namespace NotesBot.Dialogs -{ - - [LuisModel("", "YOUR_SUBSCRIPTION_KEY", domain: "westus.api.cognitive.microsoft.com")] - [Serializable] - public class SimpleNoteDialog : LuisDialog - { - // Store notes in a dictionary that uses the title as a key - private readonly Dictionary noteByTitle = new Dictionary(); - - // Default note title - public const string DefaultNoteTitle = "default"; - - // Name of note title entity - public const string Entity_Note_Title = "Note.Title"; - - /// - /// This method overload inspects the result from LUIS to see if a title entity was detected, and finds the note with that title, or the note with the default title, if no title entity was found. - /// - /// The result from LUIS that contains intents and entities that LUIS recognized. - /// This parameter returns any note that is found in the list of notes that has a matching title. - /// true if a note was found, otherwise false - public bool TryFindNote(LuisResult result, out Note note) - { - note = null; - - string titleToFind; - - EntityRecommendation title; - if (result.TryFindEntity(Entity_Note_Title, out title)) - { - titleToFind = title.Entity; - } - else - { - titleToFind = DefaultNoteTitle; - } - - return this.noteByTitle.TryGetValue(titleToFind, out note); // TryGetValue returns false if no match is found. - } - - /// - /// This method overload takes a string and finds the note with that title. - /// - /// A string containing the title of the note to search for. - /// This parameter returns any note that is found in the list of notes that has a matching title. - /// true if a note was found, otherwise false - public bool TryFindNote(string noteTitle, out Note note) - { - bool foundNote = this.noteByTitle.TryGetValue(noteTitle, out note); // TryGetValue returns false if no match is found. - return foundNote; - } - - - /// - /// Send a generic help message if an intent without an intent handler is detected. - /// - /// Dialog context. - /// The result from LUIS. - [LuisIntent("")] - public async Task None(IDialogContext context, LuisResult result) - { - - string message = $"I'm the Notes bot. I can understand requests to create, delete, and read notes. \n\n Detected intent: " + string.Join(", ", result.Intents.Select(i => i.Intent)); - await context.PostAsync(message); - context.Wait(MessageReceived); - } - - /// - /// Handle the Note.Delete intent. If a title isn't detected in the LUIS result, prompt the user for a title. - /// - /// Dialog context. - /// The result from LUIS. - [LuisIntent("Note.Delete")] - public async Task DeleteNote(IDialogContext context, LuisResult result) - { - Note note; - if (TryFindNote(result, out note)) - { - this.noteByTitle.Remove(note.Title); - await context.PostAsync($"Note {note.Title} deleted"); - } - else - { - // Prompt the user for a note title - PromptDialog.Text(context, After_DeleteTitlePrompt, "What is the title of the note you want to delete?"); - } - - } - - // Try to delete note that the user specified in response to the prompt. - private async Task After_DeleteTitlePrompt(IDialogContext context, IAwaitable result) - { - Note note; - string titleToDelete = await result; - bool foundNote = this.noteByTitle.TryGetValue(titleToDelete, out note); - - if (foundNote) - { - this.noteByTitle.Remove(note.Title); - await context.PostAsync($"Note {note.Title} deleted"); - } - else - { - await context.PostAsync($"Did not find note named {titleToDelete}."); - } - - context.Wait(MessageReceived); - } - - /// - /// Handles the Note.ReadAloud intent by displaying a note or notes. - /// If a title of an existing note is found in the LuisResult, that note is displayed. - /// If no title is detected in the LuisResult, all of the notes are displayed. - /// - /// Dialog context. - /// LUIS result. - [LuisIntent("Note.ReadAloud")] - public async Task ReadNote(IDialogContext context, LuisResult result) - { - Note note; - if (TryFindNote(result, out note)) - { - await context.PostAsync($"**{note.Title}**: {note.Text}."); - } - else - { - // Print out all the notes if no specific note name was detected - string NoteList = "Here's the list of all notes: \n\n"; - foreach (KeyValuePair entry in noteByTitle) - { - Note noteInList = entry.Value; - NoteList += $"**{noteInList.Title}**: {noteInList.Text}.\n\n"; - } - await context.PostAsync(NoteList); - } - - context.Wait(MessageReceived); - } - - private Note noteToCreate; - private string currentTitle; - - /// - /// Handles the Note.Create intent. Prompts the user for the note title if the title isn't detected in the LuisResult. - /// - /// Dialog context. - /// LUIS result. - [LuisIntent("Note.Create")] - public Task CreateNote(IDialogContext context, LuisResult result) - { - EntityRecommendation title; - if (!result.TryFindEntity(Entity_Note_Title, out title)) - { - // Prompt the user for a note title - PromptDialog.Text(context, After_TitlePrompt, "What is the title of the note you want to create?"); - } - else - { - var note = new Note() { Title = title.Entity }; - noteToCreate = this.noteByTitle[note.Title] = note; - - // Prompt the user for what they want to say in the note - PromptDialog.Text(context, After_TextPrompt, "What do you want to say in your note?"); - } - - return Task.CompletedTask; - } - - // Creates a new note using the user's response to the prompt for a title - private async Task After_TitlePrompt(IDialogContext context, IAwaitable result) - { - EntityRecommendation title; - - // Set the title to the user's response - currentTitle = await result; - if (currentTitle != null) - { - title = new EntityRecommendation(type: Entity_Note_Title) { Entity = currentTitle }; - } - else - { - // Use the default note title - title = new EntityRecommendation(type: Entity_Note_Title) { Entity = DefaultNoteTitle }; - } - - // Create a new note object - var note = new Note() { Title = title.Entity }; - // Add the new note to the list of notes and also save it in order to add text to it later - noteToCreate = this.noteByTitle[note.Title] = note; - - // Prompt the user for what they want to say in the note - PromptDialog.Text(context, After_TextPrompt, "What do you want to say in your note?"); - - } - - // Sets the text of a newly created note using the user's response to the prompt for the text of the note. - private async Task After_TextPrompt(IDialogContext context, IAwaitable result) - { - // Set the text of the note - noteToCreate.Text = await result; - - await context.PostAsync($"Created note **{this.noteToCreate.Title}** that says \"{this.noteToCreate.Text}\"."); - - context.Wait(MessageReceived); - } - - - public SimpleNoteDialog() - { - - } - - public SimpleNoteDialog(ILuisService service) - : base(service) - { - } - - [Serializable] - public sealed class Note : IEquatable - { - - public string Title { get; set; } - public string Text { get; set; } - - public override string ToString() - { - return $"[{this.Title} : {this.Text}]"; - } - - public bool Equals(Note other) - { - return other != null - && this.Text == other.Text - && this.Title == other.Title; - } - - public override bool Equals(object other) - { - return Equals(other as Note); - } - - public override int GetHashCode() - { - return this.Title.GetHashCode(); - } - } - } - - -} -// - -// -public async Task Post([FromBody]Activity activity) -{ - if (activity.Type == ActivityTypes.Message) - { - await Conversation.SendAsync(activity, () => new Dialogs.SimpleNoteDialog()); - } - else - { - HandleSystemMessage(activity); - } - var response = Request.CreateResponse(HttpStatusCode.OK); - return response; -} -// - -// -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Luis; -using Microsoft.Bot.Builder.Luis.Models; - -namespace Microsoft.Bot.Sample.SimpleAlarmBot -{ - // domain defaults to westus2.api.cognitive.microsoft.com if not provided - [LuisModel("c413b2ef-382c-45bd-8ff0-f76d60e2a821", "6d0966209c6e4f6b835ce34492f3e6d9", domain: "westus2.api.cognitive.microsoft.com", staging: true)] - [Serializable] - public class SimpleAlarmDialog : LuisDialog - { - private readonly Dictionary alarmByWhat = new Dictionary(); - - public const string DefaultAlarmWhat = "default"; - - public bool TryFindAlarm(LuisResult result, out Alarm alarm) - { - alarm = null; - - string what; - - EntityRecommendation title; - if (result.TryFindEntity(Entity_Alarm_Title, out title)) - { - what = title.Entity; - } - else - { - what = DefaultAlarmWhat; - } - - return this.alarmByWhat.TryGetValue(what, out alarm); - } - - public const string Entity_Alarm_Title = "builtin.alarm.title"; - public const string Entity_Alarm_Start_Time = "builtin.alarm.start_time"; - public const string Entity_Alarm_Start_Date = "builtin.alarm.start_date"; - - [LuisIntent("")] - public async Task None(IDialogContext context, LuisResult result) - { - string message = $"Sorry I did not understand: " + string.Join(", ", result.Intents.Select(i => i.Intent)); - await context.PostAsync(message); - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.delete_alarm")] - public async Task DeleteAlarm(IDialogContext context, LuisResult result) - { - Alarm alarm; - if (TryFindAlarm(result, out alarm)) - { - this.alarmByWhat.Remove(alarm.What); - await context.PostAsync($"alarm {alarm} deleted"); - } - else - { - await context.PostAsync("did not find alarm"); - } - - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.find_alarm")] - public async Task FindAlarm(IDialogContext context, LuisResult result) - { - Alarm alarm; - if (TryFindAlarm(result, out alarm)) - { - await context.PostAsync($"found alarm {alarm}"); - } - else - { - await context.PostAsync("did not find alarm"); - } - - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.set_alarm")] - public async Task SetAlarm(IDialogContext context, LuisResult result) - { - EntityRecommendation title; - if (!result.TryFindEntity(Entity_Alarm_Title, out title)) - { - title = new EntityRecommendation(type: Entity_Alarm_Title) { Entity = DefaultAlarmWhat }; - } - - EntityRecommendation date; - if (!result.TryFindEntity(Entity_Alarm_Start_Date, out date)) - { - date = new EntityRecommendation(type: Entity_Alarm_Start_Date) { Entity = string.Empty }; - } - - EntityRecommendation time; - if (!result.TryFindEntity(Entity_Alarm_Start_Time, out time)) - { - time = new EntityRecommendation(type: Entity_Alarm_Start_Time) { Entity = string.Empty }; - } - - var parser = new Chronic.Parser(); - var span = parser.Parse(date.Entity + " " + time.Entity); - - if (span != null) - { - var when = span.Start ?? span.End; - var alarm = new Alarm() { What = title.Entity, When = when.Value }; - this.alarmByWhat[alarm.What] = alarm; - - string reply = $"alarm {alarm} created"; - await context.PostAsync(reply); - } - else - { - await context.PostAsync("could not find time for alarm"); - } - - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.snooze")] - public async Task AlarmSnooze(IDialogContext context, LuisResult result) - { - Alarm alarm; - if (TryFindAlarm(result, out alarm)) - { - alarm.When = alarm.When.Add(TimeSpan.FromMinutes(7)); - await context.PostAsync($"alarm {alarm} snoozed!"); - } - else - { - await context.PostAsync("did not find alarm"); - } - - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.time_remaining")] - public async Task TimeRemaining(IDialogContext context, LuisResult result) - { - Alarm alarm; - if (TryFindAlarm(result, out alarm)) - { - var now = DateTime.UtcNow; - if (alarm.When > now) - { - var remaining = alarm.When.Subtract(DateTime.UtcNow); - await context.PostAsync($"There is {remaining} remaining for alarm {alarm}."); - } - else - { - await context.PostAsync($"The alarm {alarm} expired already."); - } - } - else - { - await context.PostAsync("did not find alarm"); - } - - context.Wait(MessageReceived); - } - - private Alarm turnOff; - - [LuisIntent("builtin.intent.alarm.turn_off_alarm")] - public async Task TurnOffAlarm(IDialogContext context, LuisResult result) - { - if (TryFindAlarm(result, out this.turnOff)) - { - PromptDialog.Confirm(context, AfterConfirming_TurnOffAlarm, "Are you sure?", promptStyle: PromptStyle.None); - } - else - { - await context.PostAsync("did not find alarm"); - context.Wait(MessageReceived); - } - } - - public async Task AfterConfirming_TurnOffAlarm(IDialogContext context, IAwaitable confirmation) - { - if (await confirmation) - { - this.alarmByWhat.Remove(this.turnOff.What); - await context.PostAsync($"Ok, alarm {this.turnOff} disabled."); - } - else - { - await context.PostAsync("Ok! We haven't modified your alarms!"); - } - - context.Wait(MessageReceived); - } - - [LuisIntent("builtin.intent.alarm.alarm_other")] - public async Task AlarmOther(IDialogContext context, LuisResult result) - { - await context.PostAsync("what ?"); - context.Wait(MessageReceived); - } - - public SimpleAlarmDialog() - { - - } - - public SimpleAlarmDialog(ILuisService service) - : base(service) - { - } - - [Serializable] - public sealed class Alarm : IEquatable - { - public DateTime When { get; set; } - public string What { get; set; } - - public override string ToString() - { - return $"[{this.What} at {this.When}]"; - } - - public bool Equals(Alarm other) - { - return other != null - && this.When == other.When - && this.What == other.What; - } - - public override bool Equals(object other) - { - return Equals(other as Alarm); - } - - public override int GetHashCode() - { - return this.What.GetHashCode(); - } - } - } -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-request-payment.cs b/articles/includes/code/dotnet-request-payment.cs deleted file mode 100644 index 71204d84c..000000000 --- a/articles/includes/code/dotnet-request-payment.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -var replyMessage = context.MakeMessage(); - -replyMessage.Attachments = new List(); - -var displayedItem = await new CatalogService().GetRandomItemAsync(); - -var cartId = displayedItem.Id.ToString(); -context.ConversationData.SetValue(CART_KEY, cartId); -context.ConversationData.SetValue(cartId, context.Activity.From.Id); - -var heroCard = new HeroCard -{ - Title = displayedItem.Title, - Subtitle = $"{displayedItem.Currency} {displayedItem.Price.ToString("F")}", - Text = displayedItem.Description, - Images = new List - { - new CardImage - { - Url = displayedItem.ImageUrl - } - }, - Buttons = new List - { - new CardAction - { - Title = "Buy", - Type = PaymentRequest.PaymentActionType, - Value = BuildPaymentRequest(cartId, displayedItem, MethodData) - } - } -}; - -replyMessage.Attachments.Add(heroCard.ToAttachment()); - -await context.PostAsync(replyMessage); -// - - -// -[MethodBind] -[ScorableGroup(1)] -private async Task OnInvoke(IInvokeActivity invoke, IConnectorClient connectorClient, IStateClient stateClient, HttpResponseMessage response, CancellationToken token) -{ - MicrosoftAppCredentials.TrustServiceUrl(invoke.RelatesTo.ServiceUrl); - - var jobject = invoke.Value as JObject; - if (jobject == null) - { - throw new ArgumentException("Request payload must be a valid json object."); - } - - // This is a temporary workaround for the issue that the channelId for "webchat" is mapped to "directline" in the incoming RelatesTo object - invoke.RelatesTo.ChannelId = (invoke.RelatesTo.ChannelId == "directline") ? "webchat" : invoke.RelatesTo.ChannelId; - - if (invoke.RelatesTo.User == null) - { - // Bot keeps the userId in context.ConversationData[cartId] - var conversationData = await stateClient.BotState.GetConversationDataAsync(invoke.RelatesTo.ChannelId, invoke.RelatesTo.Conversation.Id, token); - var cartId = conversationData.GetProperty(RootDialog.CARTKEY); - - if (!string.IsNullOrEmpty(cartId)) - { - invoke.RelatesTo.User = new ChannelAccount - { - Id = conversationData.GetProperty(cartId) - }; - } - } - - var updateResponse = default(object); - - switch (invoke.Name) - { - case PaymentOperations.UpdateShippingAddressOperationName: - updateResponse = await ProcessShippingAddressUpdate(jobject.ToObject(), token); - break; - - case PaymentOperations.UpdateShippingOptionOperationName: - updateResponse = await ProcessShippingOptionUpdate(jobject.ToObject(), token); - break; - - case PaymentOperations.PaymentCompleteOperationName: - updateResponse = await ProcessPaymentComplete(invoke, jobject.ToObject(), token); - break; - - default: - throw new ArgumentException("Invoke activity name is not a supported request type."); - } - - response.Content = new ObjectContent( - updateResponse, - this.Configuration.Formatters.JsonFormatter, - JsonMediaTypeFormatter.DefaultMediaType); - - response.StatusCode = HttpStatusCode.OK; -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-security.cs b/articles/includes/code/dotnet-security.cs deleted file mode 100644 index bca7a69b9..000000000 --- a/articles/includes/code/dotnet-security.cs +++ /dev/null @@ -1,13 +0,0 @@ -// -[BotAuthentication] -public class MessagesController : ApiController -{ -} -// - -// -[BotAuthentication(MicrosoftAppId = "_appIdValue_", MicrosoftAppPassword="_passwordValue_")] -public class MessagesController : ApiController -{ -} -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-send-and-receive.cs b/articles/includes/code/dotnet-send-and-receive.cs deleted file mode 100644 index 9c5bd9dd0..000000000 --- a/articles/includes/code/dotnet-send-and-receive.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -public async Task Post([FromBody]Activity activity) -{ - var connector = new ConnectorClient(new Uri(activity.ServiceUrl)); - . . . -} -// - -// -Activity reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters"); -// - -// -await connector.Conversations.ReplyToActivityAsync(reply); -// - -// -await connector.Conversations.SendToConversationAsync((Activity)newMessage); -// - -// -var userAccount = new ChannelAccount(name: "Larry", id: "@UV357341"); -var connector = new ConnectorClient(new Uri(activity.ServiceUrl)); -var conversationId = await connector.Conversations.CreateDirectConversationAsync(botAccount, userAccount); - -IMessageActivity message = Activity.CreateMessageActivity(); -message.From = botAccount; -message.Recipient = userAccount; -message.Conversation = new ConversationAccount(id: conversationId.Id); -message.Text = "Hello, Larry!"; -message.Locale = "en-Us"; -await connector.Conversations.SendToConversationAsync((Activity)message); -// - -// -var connector = new ConnectorClient(new Uri(incomingMessage.ServiceUrl)); - -List participants = new List(); -participants.Add(new ChannelAccount("joe@contoso.com", "Joe the Engineer")); -participants.Add(new ChannelAccount("sara@contoso.com", "Sara in Finance")); - -ConversationParameters cpMessage = new ConversationParameters(message.Recipient, true, participants, "Quarter End Discussion"); -var conversationId = await connector.Conversations.CreateConversationAsync(cpMessage); - -IMessageActivity message = Activity.CreateMessageActivity(); -message.From = botAccount; -message.Recipient = new ChannelAccount("lydia@contoso.com", "Lydia the CFO")); -message.Conversation = new ConversationAccount(id: conversationId.Id); -message.ChannelId = incomingMessage.ChannelId; -message.Text = "Hello, everyone!"; -message.Locale = "en-Us"; - -await connector.Conversations.SendToConversationAsync((Activity)message); -// diff --git a/articles/includes/code/dotnet-state.cs b/articles/includes/code/dotnet-state.cs deleted file mode 100644 index 643b6ae02..000000000 --- a/articles/includes/code/dotnet-state.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -StateClient stateClient = activity.GetStateClient(); -// - - -// -StateClient stateClient = new StateClient(new MicrosoftAppCredentials(microsoftAppId, microsoftAppPassword)); -// - - -// -BotData userData = await stateClient.BotState.GetUserDataAsync(activity.ChannelId, activity.From.Id); -var sentGreeting = userData.GetProperty("SentGreeting"); -// - - -// -MyCustomType myUserData = new MyCustomType(); -BotData botData = await botState.GetUserDataAsync(activity.ChannelId, activity.From.Id); -myUserData = botData.GetProperty("UserData"); -// - - -// -BotData userData = await stateClient.BotState.GetUserDataAsync(activity.ChannelId, activity.From.Id); -userData.SetProperty("SentGreeting", true); -await stateClient.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData); -// - - -// -BotData userData = await stateClient.BotState.GetUserDataAsync(activity.ChannelId, activity.From.Id); -userData.SetProperty("UserData", myUserData); -await stateClient.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData); -// - - -// -var builder = new ContainerBuilder(); -builder - .Register(c => new CachingBotDataStore(c.Resolve(), CachingBotDataStoreConsistencyPolicy.LastWriteWins)) - .As>() - .AsSelf() - .InstancePerLifetimeScope(); -builder.Update(Conversation.Container); -// \ No newline at end of file diff --git a/articles/includes/code/dotnet-text-to-speech.cs b/articles/includes/code/dotnet-text-to-speech.cs deleted file mode 100644 index d7a8098b9..000000000 --- a/articles/includes/code/dotnet-text-to-speech.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -Activity reply = activity.CreateReply("This is the text that will be displayed."); -reply.Speak = "This is the text that will be spoken."; -reply.InputHint = InputHints.AcceptingInput; -await connector.Conversations.ReplyToActivityAsync(reply); -// - - - -// -await context.SayAsync(text: "Thank you for your order!", speak: "Thank you for your order!"); -// - - - -// -PromptDialog.Confirm(context, AfterResetAsync,  - new PromptOptions(prompt: "Are you sure that you want to cancel this transaction?",  - speak: "Are you sure that you want to cancel this transaction?", - retrySpeak: "Are you sure that you want to cancel this transaction?")); -// diff --git a/articles/includes/code/intelligence-location-control.cs b/articles/includes/code/intelligence-location-control.cs deleted file mode 100644 index 2a8686636..000000000 --- a/articles/includes/code/intelligence-location-control.cs +++ /dev/null @@ -1,6 +0,0 @@ -// -var apiKey = WebConfigurationManager.AppSettings["BingMapsApiKey"]; -var prompt = "Where should I ship your order? Type or say an address."; -var locationDialog = new LocationDialog(apiKey, message.ChannelId, prompt, LocationOptions.None, LocationRequiredFields.StreetAddress | LocationRequiredFields.PostalCode); -context.Call(locationDialog, (dialogContext, result) => {...}); -// diff --git a/articles/includes/code/intelligence-location-control.js b/articles/includes/code/intelligence-location-control.js deleted file mode 100644 index 9860e9f09..000000000 --- a/articles/includes/code/intelligence-location-control.js +++ /dev/null @@ -1,36 +0,0 @@ -// -var options = { - prompt: "Where should I ship your order? Type or say an address.", - requiredFields: - locationDialog.LocationRequiredFields.streetAddress | - locationDialog.LocationRequiredFields.postalCode -} -locationDialog.getLocation(session, options); -// - -// -locationDialog.create(bot); - -bot.dialog("/", [ - function (session) { - locationDialog.getLocation(session, { - prompt: "Where should I ship your order? Type or say an address.", - requiredFields: - locationDialog.LocationRequiredFields.streetAddress | - locationDialog.LocationRequiredFields.locality | - locationDialog.LocationRequiredFields.region | - locationDialog.LocationRequiredFields.postalCode | - locationDialog.LocationRequiredFields.country - }); - }, - function (session, results) { - if (results.response) { - var place = results.response; - session.send(place.streetAddress + ", " + place.locality + ", " + place.region + ", " + place.country + " (" + place.postalCode + ")"); - } - else { - session.send("OK, I won't be shipping it"); - } - } -]); -// diff --git a/articles/includes/code/node-basicNote-intentDialog.js b/articles/includes/code/node-basicNote-intentDialog.js deleted file mode 100644 index 1460dbd42..000000000 --- a/articles/includes/code/node-basicNote-intentDialog.js +++ /dev/null @@ -1,195 +0,0 @@ - -/*----------------------------------------------------------------------------- - -This bot demonstrates how to use dialogs with a LuisRecognizer to add LUIS support to a bot. -LUIS identifies intents and entities from user messages, or utterances. - -Intents map utterances to functionality in your bot. -In this example, the intents provide the following mappings: - * The Notes.Create intent maps to the CreateNote dialog - * The Notes.Delete intent maps to the DeleteNote dialog - * The Notes.ReadAloud intent maps to the ReadNote dialog - ------------------------------------------------------------------------------*/ -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 -// See https://aka.ms/node-env-var for information on setting environment variables in launch.json if you are using VSCode -var connector = new builder.ChatConnector({ - appId: process.env.MICROSOFT_APP_ID, - appPassword: process.env.MICROSOFT_APP_PASSWORD -}); - -// Listen for messages from users -server.post('/api/messages', connector.listen()); - -// -// Create your bot -var bot = new builder.UniversalBot(connector); -// - - -// -// Add global LUIS recognizer to bot -var luisAppUrl = process.env.LUIS_APP_URL || 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/?subscription-key='; -var notesRecognizer = bot.recognizer(new builder.LuisRecognizer(luisAppUrl)); -// - -// -var noteIntentsDialog = new builder.IntentDialog({ - recognizers: [notesRecognizer] -}); - -bot.dialog('/', noteIntentsDialog); -// - -// -// Handle the None intent -// This default message handler is invoked if the user's utterance doesn't -// match any other intents defined in the LUIS app. -noteIntentsDialog.matches('None', [ - function (session, args) { - session.send("Hi... I'm the note bot sample. I can create new notes, read saved notes to you and delete notes."); - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing userData.notes in default message handler"); - } -}]); -// - -// -// Handle the Note.Create intent -noteIntentsDialog.matches('Note.Create', [ - function (session, args, next) { - // Resolve and store any Note.Title entity passed from LUIS. - var intent = args.intent; - var title = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - - var note = session.dialogData.note = { - title: title ? title.entity : null, - }; - - // Prompt for title - if (!note.title) { - builder.Prompts.text(session, 'What would you like to call your note?'); - } else { - next(); - } - }, - function (session, results, next) { - var note = session.dialogData.note; - if (results.response) { - note.title = results.response; - } - - // Prompt for the text of the note - if (!note.text) { - builder.Prompts.text(session, 'What would you like to say in your note?'); - } else { - next(); - } - }, - function (session, results) { - var note = session.dialogData.note; - if (results.response) { - note.text = results.response; - } - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing session.userData.notes in CreateNote dialog"); - } - // Save notes in the notes object - session.userData.notes[note.title] = note; - - // Send confirmation to user - session.endDialog('Creating note named "%s" with text "%s"', - note.title, note.text); - } -]); -// - -// -// Handle the Note.Delete intent -noteIntentsDialog.matches('Note.Delete', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify that the title is in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to delete?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to delete."); - } - }, - function (session, results) { - delete session.userData.notes[results.response.entity]; - session.endDialog("Deleted the '%s' note.", results.response.entity); - } -]); -// - -// -// Handle the Notes.ReadAloud intent -noteIntentsDialog.matches('Note.ReadAloud', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify it's in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to read?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to read."); - } - }, - function (session, results) { - session.endDialog("Here's the '%s' note: '%s'.", results.response.entity, session.userData.notes[results.response.entity].text); - } -]); -// - -// -// Helper function to count the number of notes stored in session.userData.notes -function noteCount(notes) { - - var i = 0; - for (var name in notes) { - i++; - } - return i; -} -// diff --git a/articles/includes/code/node-basicNote.js b/articles/includes/code/node-basicNote.js deleted file mode 100644 index 539190c5c..000000000 --- a/articles/includes/code/node-basicNote.js +++ /dev/null @@ -1,194 +0,0 @@ - -/*----------------------------------------------------------------------------- - -This bot demonstrates how to use dialogs with a LuisRecognizer to add LUIS support to a bot. -LUIS identifies intents and entities from user messages, or utterances. - -Intents map utterances to functionality in your bot. -In this example, the intents provide the following mappings: - * The Notes.Create intent maps to the CreateNote dialog - * The Notes.Delete intent maps to the DeleteNote dialog - * The Notes.ReadAloud intent maps to the ReadNote dialog - ------------------------------------------------------------------------------*/ -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 -// See https://aka.ms/node-env-var for information on setting environment variables in launch.json if you are using VSCode -var connector = new builder.ChatConnector({ - appId: process.env.MICROSOFT_APP_ID, - appPassword: process.env.MICROSOFT_APP_PASSWORD -}); - -// Listen for messages from users -server.post('/api/messages', connector.listen()); - -// -// Create your bot with a function to receive messages from the user. -// This default message handler is invoked if the user's utterance doesn't -// match any intents handled by other dialogs. -var bot = new builder.UniversalBot(connector, function (session, args) { - session.send("Hi... I'm the note bot sample. I can create new notes, read saved notes to you and delete notes."); - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing userData.notes in default message handler"); - } -}); -// - -// -// Add global LUIS recognizer to bot -var luisAppUrl = process.env.LUIS_APP_URL || 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/?subscription-key='; -bot.recognizer(new builder.LuisRecognizer(luisAppUrl)); -// - -// -// CreateNote dialog -bot.dialog('CreateNote', [ - function (session, args, next) { - // Resolve and store any Note.Title entity passed from LUIS. - var intent = args.intent; - var title = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - - var note = session.dialogData.note = { - title: title ? title.entity : null, - }; - - // Prompt for title - if (!note.title) { - builder.Prompts.text(session, 'What would you like to call your note?'); - } else { - next(); - } - }, - function (session, results, next) { - var note = session.dialogData.note; - if (results.response) { - note.title = results.response; - } - - // Prompt for the text of the note - if (!note.text) { - builder.Prompts.text(session, 'What would you like to say in your note?'); - } else { - next(); - } - }, - function (session, results) { - var note = session.dialogData.note; - if (results.response) { - note.text = results.response; - } - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing session.userData.notes in CreateNote dialog"); - } - // Save notes in the notes object - session.userData.notes[note.title] = note; - - // Send confirmation to user - session.endDialog('Creating note named "%s" with text "%s"', - note.title, note.text); - } -]).triggerAction({ - matches: 'Note.Create', - confirmPrompt: "This will cancel the creation of the note you started. Are you sure?" -}).cancelAction('cancelCreateNote', "Note canceled.", { - matches: /^(cancel|nevermind)/i, - confirmPrompt: "Are you sure?" -}); -// - -// -// Delete note dialog -bot.dialog('DeleteNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify that the title is in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to delete?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to delete."); - } - }, - function (session, results) { - delete session.userData.notes[results.response.entity]; - session.endDialog("Deleted the '%s' note.", results.response.entity); - } -]).triggerAction({ - matches: 'Note.Delete' -}).cancelAction('cancelDeleteNote', "Ok - canceled note deletion.", { - matches: /^(cancel|nevermind)/i -}); -// - -// -// Read note dialog -bot.dialog('ReadNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify it's in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to read?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to read."); - } - }, - function (session, results) { - session.endDialog("Here's the '%s' note: '%s'.", results.response.entity, session.userData.notes[results.response.entity].text); - } -]).triggerAction({ - matches: 'Note.ReadAloud' -}).cancelAction('cancelReadNote', "Ok.", { - matches: /^(cancel|nevermind)/i -}); -// - -// -// Helper function to count the number of notes stored in session.userData.notes -function noteCount(notes) { - - var i = 0; - for (var name in notes) { - i++; - } - return i; -} -// diff --git a/articles/includes/code/node-getstarted.js b/articles/includes/code/node-getstarted.js deleted file mode 100644 index 011be9993..000000000 --- a/articles/includes/code/node-getstarted.js +++ /dev/null @@ -1,35 +0,0 @@ -// - -var builder = require('botbuilder'); - -var connector = new builder.ConsoleConnector().listen(); -var bot = new builder.UniversalBot(connector, function (session) { - session.send("You said: %s", session.message.text); -}); -// - -// - -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 -}); - -// Listen for messages from users -server.post('/api/messages', connector.listen()); - -// Receive messages from the user and respond by echoing each message back (prefixed with 'You said:') -var bot = new builder.UniversalBot(connector, function (session) { - session.send("You said: %s", session.message.text); -}); -// diff --git a/articles/includes/code/node-howto-recognize-intent.js b/articles/includes/code/node-howto-recognize-intent.js deleted file mode 100644 index a5fa2768b..000000000 --- a/articles/includes/code/node-howto-recognize-intent.js +++ /dev/null @@ -1,41 +0,0 @@ -// -var builder = require('../../core/'); - -// Create bot and default message handler -var connector = new builder.ConsoleConnector().listen(); -var bot = new builder.UniversalBot(connector, function (session) { - session.send("You said: '%s'. Try asking for 'help' or say 'goodbye' to quit", session.message.text); -}); - - -// Install a custom recognizer to look for user saying 'help' or 'goodbye'. -bot.recognizer({ - recognize: function (context, done) { - var intent = { score: 0.0 }; - - if (context.message.text) { - switch (context.message.text.toLowerCase()) { - case 'help': - intent = { score: 1.0, intent: 'Help' }; - break; - case 'goodbye': - intent = { score: 1.0, intent: 'Goodbye' }; - break; - } - } - done(null, intent); - } -}); - -// - -// -// Add a help dialog with a trigger action that is bound to the 'Help' intent -bot.dialog('helpDialog', function (session) { - session.endDialog("This bot will echo back anything you say. Say 'goodbye' to quit."); -}).triggerAction({ matches: 'Help' }); - - -// Add a global endConversation() action that is bound to the 'Goodbye' intent -bot.endConversationAction('goodbyeAction', "Ok... See you later.", { matches: 'Goodbye' }); -// \ No newline at end of file diff --git a/articles/includes/code/node-input-hints.js b/articles/includes/code/node-input-hints.js deleted file mode 100644 index a5ec2ae4a..000000000 --- a/articles/includes/code/node-input-hints.js +++ /dev/null @@ -1,25 +0,0 @@ -// -var msg = new builder.Message(session) - .speak('This is the text that will be spoken.') - .inputHint(builder.InputHint.acceptingInput); -session.send(msg).endDialog(); -// - - - -// -session.say('Please hold while I calculate a response. Thanks!', - 'Please hold while I calculate a response. Thanks!', - { inputHint: builder.InputHint.ignoringInput } -); -// - - - -// -builder.Prompts.text(session, 'This is the text that will be displayed.', { - speak: 'This is the text that will be spoken initially.', - retrySpeak: 'This is the text that is spoken after waiting a while for user input.', - inputHint: builder.InputHint.expectingInput -}); -// diff --git a/articles/includes/code/node-regex-recognizer.js b/articles/includes/code/node-regex-recognizer.js deleted file mode 100644 index c0210b4a2..000000000 --- a/articles/includes/code/node-regex-recognizer.js +++ /dev/null @@ -1,147 +0,0 @@ -/*----------------------------------------------------------------------------- -Note - this sample should only be used for snippets. -Don't use the whole sample as is. -Tested that code compiles 5/23. ------------------------------------------------------------------------------*/ - -var builder = require('../../core/'); - -// Create bot and bind to console -var connector = new builder.ConsoleConnector().listen(); - -// -var bot = new builder.UniversalBot(connector, function (session) { - session.send("Hi... I'm a sample bot."); -}); - -// Add a global LUIS recognizer to the bot by using the endpoint URL of the LUIS app -var model = 'https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/c413b2ef-382c-45bd-8ff0-f76d60e2a821?subscription-key=6d0966209c6e4f6b835ce34492f3e6d9'; -bot.recognizer(new builder.LuisRecognizer(model)); -// - -// -bot.recognizer(new builder.RegExpRecognizer( "CancelIntent", { en_us: /^(cancel|nevermind)/i, ja_jp: /^(キャンセル)/ })); -// - -// -bot.dialog('CancelDialog', function (session) { - session.endConversation("Ok, I'm canceling your order."); -}).triggerAction({ matches: 'CancelIntent' }); -// - -// Set Alarm dialog -bot.dialog('/setAlarm', [ - function (session, args, next) { - // Resolve and store any entities passed from LUIS. - var intent = args.intent; - var title = builder.EntityRecognizer.findEntity(intent.entities, 'builtin.alarm.title'); - var time = builder.EntityRecognizer.resolveTime(intent.entities); - var alarm = session.dialogData.alarm = { - title: title ? title.entity : null, - timestamp: time ? time.getTime() : null - }; - - // Prompt for title - if (!alarm.title) { - builder.Prompts.text(session, 'What would you like to call your alarm?'); - } else { - next(); - } - }, - function (session, results, next) { - var alarm = session.dialogData.alarm; - if (results.response) { - alarm.title = results.response; - } - - // Prompt for time - if (!alarm.timestamp) { - builder.Prompts.time(session, 'What time would you like to set the alarm for?'); - } else { - next(); - } - }, - function (session, results) { - var alarm = session.dialogData.alarm; - if (results.response) { - var time = builder.EntityRecognizer.resolveTime([results.response]); - alarm.timestamp = time ? time.getTime() : null; - } - - // Save address of who to notify and write to scheduler. - alarm.address = session.message.address; - alarms[alarm.title] = alarm; - - // Send confirmation to user - var date = new Date(alarm.timestamp); - var isAM = date.getHours() < 12; - session.endDialog('Creating alarm named "%s" for %d/%d/%d %d:%02d%s', - alarm.title, - date.getMonth() + 1, date.getDate(), date.getFullYear(), - isAM ? date.getHours() : date.getHours() - 12, date.getMinutes(), isAM ? 'am' : 'pm'); - } -]).triggerAction({ - matches: 'builtin.intent.alarm.set_alarm', - confirmPrompt: "This will cancel the current alarm. Are you sure?" -}).cancelAction('cancelSetAlarm', "Alarm canceled.", { - matches: /^(cancel|nevermind)/i, - confirmPrompt: "Are you sure?" -}); - -// Delete Alarm dialog -bot.dialog('/deleteAlarm', [ - function (session, args, next) { - if (alarmCount() > 0) { - // Resolve entities passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'builtin.alarm.title'); - if (entity) { - // Verify its in our set of alarms. - title = builder.EntityRecognizer.findBestMatch(alarms, entity.entity); - } - - // Prompt for alarm name - if (!title) { - builder.Prompts.choice(session, 'Which alarm would you like to delete?', alarms); - } else { - next({ response: title }); - } - } else { - session.endDialog("No alarms to delete."); - } - }, - function (session, results) { - delete alarms[results.response.entity]; - session.endDialog("Deleted the '%s' alarm.", results.response.entity); - } -]).triggerAction({ - matches: 'builtin.intent.alarm.delete_alarm' -}).cancelAction('cancelDeleteAlarm', "Ok.", { - matches: 'CancelIntent' /* /^(cancel|nevermind)/i */ -}); - -// Very simple alarm scheduler -var alarms = {}; -setInterval(function () { - var now = new Date().getTime(); - for (var key in alarms) { - var alarm = alarms[key]; - if (now >= alarm.timestamp) { - var msg = new builder.Message() - .address(alarm.address) - .text("Here's your '%s' alarm.", alarm.title); - bot.send(msg); - delete alarms[key]; - } - } -}, 15000); - -// Helpers -function alarmCount() { - var i = 0; - for (var name in alarms) { - i++; - } - return i; -} diff --git a/articles/includes/code/node-request-payment.js b/articles/includes/code/node-request-payment.js deleted file mode 100644 index 35d43b0cb..000000000 --- a/articles/includes/code/node-request-payment.js +++ /dev/null @@ -1,116 +0,0 @@ -// -var bot = new builder.UniversalBot(connector, (session) => { - - catalog.getPromotedItem().then(product => { - - // Store userId for later, when reading relatedTo to resume dialog with the receipt. - var cartId = product.id; - session.conversationData[CartIdKey] = cartId; - session.conversationData[cartId] = session.message.address.user.id; - - // Create PaymentRequest obj based on product information. - var paymentRequest = createPaymentRequest(cartId, product); - - var buyCard = new builder.HeroCard(session) - .title(product.name) - .subtitle(util.format('%s %s', product.currency, product.price)) - .text(product.description) - .images([ - new builder.CardImage(session).url(product.imageUrl) - ]) - .buttons([ - new builder.CardAction(session) - .title('Buy') - .type(payments.PaymentActionType) - .value(paymentRequest) - ]); - - session.send(new builder.Message(session) - .addAttachment(buyCard)); - }); -}); -// - - -// -connector.onInvoke((invoke, callback) => { - console.log('onInvoke', invoke); - - // This is a temporary workaround for the issue that the channelId for "webchat" is mapped to "directline" in the incoming RelatesTo object - invoke.relatesTo.channelId = invoke.relatesTo.channelId === 'directline' ? 'webchat' : invoke.relatesTo.channelId; - - var storageCtx = { - address: invoke.relatesTo, - persistConversationData: true, - conversationId: invoke.relatesTo.conversation.id - }; - - connector.getData(storageCtx, (err, data) => { - var cartId = data.conversationData[CartIdKey]; - if (!invoke.relatesTo.user && cartId) { - // Bot keeps the userId in context.ConversationData[cartId] - var userId = data.conversationData[cartId]; - invoke.relatesTo.useAuth = true; - invoke.relatesTo.user = { id: userId }; - } - - // Continue based on PaymentRequest event. - var paymentRequest = null; - switch (invoke.name) { - case payments.Operations.UpdateShippingAddressOperation: - case payments.Operations.UpdateShippingOptionOperation: - paymentRequest = invoke.value; - - // Validate address AND shipping method (if selected). - checkout - .validateAndCalculateDetails(paymentRequest, paymentRequest.shippingAddress, paymentRequest.shippingOption) - .then(updatedPaymentRequest => { - // Return new paymentRequest with updated details. - callback(null, updatedPaymentRequest, 200); - }).catch(err => { - // Return error to onInvoke handler. - callback(err); - // Send error message back to user. - bot.beginDialog(invoke.relatesTo, 'checkout_failed', { - errorMessage: err.message - }); - }); - - break; - - case payments.Operations.PaymentCompleteOperation: - var paymentRequestComplete = invoke.value; - paymentRequest = paymentRequestComplete.paymentRequest; - var paymentResponse = paymentRequestComplete.paymentResponse; - - // Validate address AND shipping method. - checkout - .validateAndCalculateDetails(paymentRequest, paymentResponse.shippingAddress, paymentResponse.shippingOption) - .then(updatedPaymentRequest => - // Process payment. - checkout - .processPayment(updatedPaymentRequest, paymentResponse) - .then(chargeResult => { - // Return success. - callback(null, { result: "success" }, 200); - // Send receipt to user. - bot.beginDialog(invoke.relatesTo, 'checkout_receipt', { - paymentRequest: updatedPaymentRequest, - chargeResult: chargeResult - }); - }) - ).catch(err => { - // Return error to onInvoke handler. - callback(err); - // Send error message back to user. - bot.beginDialog(invoke.relatesTo, 'checkout_failed', { - errorMessage: err.message - }); - }); - - break; - } - - }); -}); -// diff --git a/articles/includes/code/node-send-card-buttons.js b/articles/includes/code/node-send-card-buttons.js deleted file mode 100644 index ab17432b3..000000000 --- a/articles/includes/code/node-send-card-buttons.js +++ /dev/null @@ -1,71 +0,0 @@ -// -var msg = new builder.Message(session) - .addAttachment({ - contentType: "application/vnd.microsoft.card.adaptive", - content: { - type: "AdaptiveCard", - speak: "Your meeting about \"Adaptive Card design session\" is starting at 12:30pmDo you want to snooze or do you want to send a late notification to the attendees?", -              body: [ - { - "type": "TextBlock", - "text": "Adaptive Card design session", - "size": "large", - "weight": "bolder" - }, - { - "type": "TextBlock", - "text": "Conf Room 112/3377 (10)" - }, - { - "type": "TextBlock", - "text": "12:30 PM - 1:30 PM" - }, - { - "type": "TextBlock", - "text": "Snooze for" - }, - { - "type": "Input.ChoiceSet", - "id": "snooze", - "style":"compact", - "choices": [ - { - "title": "5 minutes", - "value": "5", - "isSelected": true - }, - { - "title": "15 minutes", - "value": "15" - }, - { - "title": "30 minutes", - "value": "30" - } - ] - } - ], - "actions": [ - { - "type": "Action.OpenUrl", - "method": "POST", - "url": "http://foo.com", - "title": "Snooze" - }, - { - "type": "Action.OpenUrl", - "method": "POST", - "url": "http://foo.com", - "title": "I'll be late" - }, - { - "type": "Action.OpenUrl", - "method": "POST", - "url": "http://foo.com", - "title": "Dismiss" - } - ] - } - }); -session.send(msg); -// diff --git a/articles/includes/code/node-send-suggested-actions.js b/articles/includes/code/node-send-suggested-actions.js deleted file mode 100644 index 0149fe912..000000000 --- a/articles/includes/code/node-send-suggested-actions.js +++ /dev/null @@ -1,14 +0,0 @@ -// -var msg = new builder.Message(session) - .text("Thank you for expressing interest in our premium golf shirt! What color of shirt would you like?") - .suggestedActions( - builder.SuggestedActions.create( - session, [ - builder.CardAction.imBack(session, "productId=1&color=green", "Green"), - builder.CardAction.imBack(session, "productId=1&color=blue", "Blue"), - builder.CardAction.imBack(session, "productId=1&color=red", "Red") - ] - )); -session.send(msg); -// - diff --git a/articles/includes/code/node-text-to-speech.js b/articles/includes/code/node-text-to-speech.js deleted file mode 100644 index 9d35a35ee..000000000 --- a/articles/includes/code/node-text-to-speech.js +++ /dev/null @@ -1,25 +0,0 @@ -// -var msg = new builder.Message(session) - .speak('This is the text that will be spoken.') - .inputHint(builder.InputHint.acceptingInput); -session.send(msg).endDialog(); -// - - - -// -session.say('Please hold while I calculate a response.', - 'Please hold while I calculate a response.', - { inputHint: builder.InputHint.ignoringInput } -); -// - - - -// -builder.Prompts.text(session, 'Are you sure that you want to cancel this transaction?', { - speak: 'Are you sure that you want to cancel this transaction?', - retrySpeak: 'Are you sure that you want to cancel this transaction?', - inputHint: builder.InputHint.expectingInput -}); -// diff --git a/articles/includes/deploy/snippet-ARM-existing-resource-group.md b/articles/includes/deploy/snippet-ARM-existing-resource-group.md deleted file mode 100644 index bbff7d127..000000000 --- a/articles/includes/deploy/snippet-ARM-existing-resource-group.md +++ /dev/null @@ -1,40 +0,0 @@ -In this step, you create a bot application service which sets the deployment stage for the bot. When using an existing resource group, you can either use an existing app service plan or create a new one. Steps for both options are listed below. - -From the resulting JSON output, copy the value of the **id** field to use as the value for the **registration subscription id** in the next step. - -> [!NOTE] -> This step can take a few minutes to complete. - -**Option 1: Existing App Service Plan** - -In this case, we are using an existing App Service Plan, but creating new a Web App and Bot Channels Registration. - -> [!NOTE] -> This command sets the bot's ID and display name. The `botId` parameter should be globally unique and is used as the immutable bot ID. The bot's display name is mutable. - -```cmd -az group deployment create --resource-group "" --template-file "" --parameters appId="" appSecret="" botId="" newWebAppName="" existingAppServicePlan="" appServicePlanLocation="" --name "" -``` - -**Option 2: New App Service Plan** - -In this case, we are creating App Service Plan, Web App, and Bot Channels Registration. - -```cmd -az group deployment create --resource-group "" --template-file "" --parameters appId="" appSecret="" botId="" newWebAppName="" newAppServicePlanName="" appServicePlanLocation="" --name "" -``` - -| Option | Description | -|:---------|:------------| -| name | The display name to use for your bot channels registration. Default is the value of the `botId` parameter.| -| resource-group | Name of the azure resource group. | -| template-file | The path to the ARM template. Usually, the `template-with-preexisting-rg.json` file is provided in the `deploymentTemplates` folder of the project. This is a path to an existing template file. It can be an absolute path, or relative to the current directory. All bot templates generate ARM template files.| -| location |Location. Values from: `az account list-locations`. You can configure the default location using `az configure --defaults location=`. | -| parameters | Deployment parameters, provided as a list of key=value pairs. Enter the following parameter values: - -- `appId` - The *app id* value generated by the previous step. -- `appSecret` - The password you provided in the previous step. -- `botId` - A name for the Bot Channels Registration resource to create. It must be globally unique. It is used as the immutable bot ID. It is also used as the default display name, which is mutable. -- `newWebAppName` - A name for the bot application service. -- `newAppServicePlanName` - A name for the application service plan resource to create. -- `newAppServicePlanLocation` - The location of the application service plan. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-ARM-new-resource-group.md b/articles/includes/deploy/snippet-ARM-new-resource-group.md deleted file mode 100644 index d5ac2e0bc..000000000 --- a/articles/includes/deploy/snippet-ARM-new-resource-group.md +++ /dev/null @@ -1,27 +0,0 @@ -In this step, you create a bot application service which sets the deployment stage for the bot. You use an ARM template, a new service plan and a new resource group. - -From the resulting JSON output, copy the numeric value of the **id** field to use as the value for the **registration subscription id** in the next step. - -> [!NOTE] -> This step can take a few minutes to complete. - -```cmd -az deployment create --template-file " --parameters appId="" appSecret="" botId="" botSku=F0 newAppServicePlanName="" newWebAppName="" groupName="" groupLocation="" newAppServicePlanLocation="" --name "" -``` - -| Option | Description | -|:---------|:------------| -| name | The display name to use for your bot channels registration. Default is the value of the `botId` parameter.| -| template-file | The path to the ARM template. Usually, the `template-with-new-rg.json` file is provided in the `deploymentTemplates` folder of the bot project. This is a path to an existing template file. It can be an absolute path, or relative to the current directory. All bot templates generate ARM template files.| -| location |Location. Values from: `az account list-locations`. You can configure the default location using `az configure --defaults location=`. | -| parameters | Deployment parameters, provided as a list of key=value pairs. Enter the following parameter values: - -- `appId` - The *app id* value generated by the previous step. -- `appSecret` - The password you provided in the previous step. -- `botId` - A name for the Bot Channels Registration resource to create. It must be globally unique. It is used as the immutable bot ID. It is also used as the default display name, which is mutable. -- `botSku` - The pricing tier; it can be F0 (Free) or S1 (Standard). -- `newAppServicePlanName` - The name of the new application service plan. -- `newWebAppName` - A name for the bot application service. -- `groupName` - A name for the new resource group. -- `groupLocation` - The location of the Azure resource group. -- `newAppServicePlanLocation` - The location of the application service plan. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-IIS-Kudu-files.md b/articles/includes/deploy/snippet-IIS-Kudu-files.md deleted file mode 100644 index 50ce3fc5a..000000000 --- a/articles/includes/deploy/snippet-IIS-Kudu-files.md +++ /dev/null @@ -1,38 +0,0 @@ - -You need to prepare your project files before you can deploy your C#, Javascript, or Typescript bot. If you are deploying a Python bot you can skip this step. - - -##### [C#](#tab/csharp) - -```cmd -az bot prepare-deploy --lang Csharp --code-dir "." --proj-file-path "MyBot.csproj" -``` - -You must provide the path to the .csproj file relative to --code-dir. This can be performed via the --proj-file-path argument. The command would resolve --code-dir and --proj-file-path to "./MyBot.csproj" - - -##### [JavaScript](#tab/javascript) - -```cmd -az bot prepare-deploy --code-dir "." --lang Javascript -``` - -This command will fetch a web.config which is needed for Node.js apps to work with IIS on Azure App Services. Make sure web.config is saved to the root of your bot. - - -##### [TypeScript](#tab/typescript) - -```cmd -az bot prepare-deploy --code-dir "." --lang Typescript -``` - -This command works similarly to JavaScript above, but for a Typescript bot. - ---- - -> [!NOTE] -> For C# bots, the `az bot prepare-deploy` command generate sa `.deployment` file in your bot project folder. -> For JavaScript bots, the command generates two `web.config` file in your project folder. -> For TypeScript bots, the command generates two `web.config` files. One is in your project folder and another in the **src** folder within your project folder. - - diff --git a/articles/includes/deploy/snippet-additional-resources.md b/articles/includes/deploy/snippet-additional-resources.md deleted file mode 100644 index 1522da000..000000000 --- a/articles/includes/deploy/snippet-additional-resources.md +++ /dev/null @@ -1,13 +0,0 @@ -When you deploy a bot, typically these resources are created in the Azure portal: - -| Resources | Description | -|----------------|-------------| -| Web App Bot | An Azure Bot Service bot that is deployed to an Azure App Service.| -| [App Service](https://docs.microsoft.com/azure/app-service/)| Enables you to build and host web applications.| -| [App Service plan](https://docs.microsoft.com/azure/app-service/azure-web-sites-web-hosting-plans-in-depth-overview)| Defines a set of compute resources for a web app to run.| - -If you create your bot through the Azure portal, you are able to provision additional resources, like [Application Insights for telemetry](~/v4sdk/bot-builder-telemetry.md). - -To see documentation on `az bot` commands, see the [reference](https://docs.microsoft.com/cli/azure/bot?view=azure-cli-latest) topic. - -If you are unfamiliar with Azure resource group, see this [terminology](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-overview#terminology) topic. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-az-create-group.md b/articles/includes/deploy/snippet-az-create-group.md deleted file mode 100644 index 797a00312..000000000 --- a/articles/includes/deploy/snippet-az-create-group.md +++ /dev/null @@ -1,8 +0,0 @@ -```cmd -az group create --name --location --verbose -``` - -| Option | Description | -|:---|:---| -| --name | A unique name for the resource group. DO NOT include spaces or underscores in the name. | -| --location | Geographic location used to create the resource group. For example, `eastus`, `westus`, `westus2`, and so on. Use `az account list-locations` for a list of locations. | \ No newline at end of file diff --git a/articles/includes/deploy/snippet-az-login.md b/articles/includes/deploy/snippet-az-login.md deleted file mode 100644 index 5121af940..000000000 --- a/articles/includes/deploy/snippet-az-login.md +++ /dev/null @@ -1,9 +0,0 @@ -Once you've created and tested a bot locally, you can deploy it to Azure. Open a command prompt to log in to the Azure portal. - -```cmd -az login -``` -A browser window will open, allowing you to sign in. - -> [!NOTE] -> If you deploy your bot to a non-Azure cloud such as US Gov, you need to run `az cloud set --name ` before `az login`, where <name-of-cloud> is the name of a registered cloud, such as `AzureUSGovernment`. If you want to go back to public cloud, you can run `az cloud set --name AzureCloud`. diff --git a/articles/includes/deploy/snippet-az-set-subscription.md b/articles/includes/deploy/snippet-az-set-subscription.md deleted file mode 100644 index 2119ed36c..000000000 --- a/articles/includes/deploy/snippet-az-set-subscription.md +++ /dev/null @@ -1,7 +0,0 @@ -Set the default subscription to use. - -```cmd -az account set --subscription "" -``` - -If you are not sure which subscription to use for deploying the bot, you can view the list of subscriptions for your account by using `az account list` command. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-clear-encryption.md b/articles/includes/deploy/snippet-clear-encryption.md deleted file mode 100644 index 66921151f..000000000 --- a/articles/includes/deploy/snippet-clear-encryption.md +++ /dev/null @@ -1,8 +0,0 @@ -Clear the encryption key setting. - -1. Log into the [Azure portal](http://portal.azure.com/). -1. Open the Web App Bot resource for your bot. -1. Open the bot's **Application Settings**. -1. In the **Application Settings** window, scroll down to the **Application settings**. -1. Locate the **botFileSecret** and delete it. -1. Click *Save* at the top of this page to save your changes. diff --git a/articles/includes/deploy/snippet-create-app-registration.md b/articles/includes/deploy/snippet-create-app-registration.md deleted file mode 100644 index 4ddfccdb3..000000000 --- a/articles/includes/deploy/snippet-create-app-registration.md +++ /dev/null @@ -1,19 +0,0 @@ -In this step you create an Azure Active Directory application, which will allow: - -- The user to interact with the bot via a set of channels such as *Web Chat*. -- The definition of *OAuth Connection Settings* to authenticate a user and to create a *token* used by the bot to access protected resources on behalf of the user. - -To create an Azure Active Directory application, execute the following command: - -```cmd -az ad app create --display-name "displayName" --password "AtLeastSixteenCharacters_0" --available-to-other-tenants -``` - -| Option | Description | -|:---------|:------------| -| display-name | The display name of the application. It is listed in the Azure portal in the general resources list and in the resource group it belongs.| -| password | The password, also known as **client secret**, for the application. This is a password you create for this resource. It must be at least 16 characters long, contain at least 1 upper or lower case alphabetical character, and contain at least 1 special character.| -| available-to-other-tenants| Indicates that the application can be used from any Azure AD tenant. Set this to enable your bot to work with the Azure Bot Service channels.| - -The above command outputs JSON with the key `appId`, copy and save it. -You are going to use this `appId` and the password you entered in the ARM deployment step, to assign values to the `appId` and the `appSecret` parameters, respectively. diff --git a/articles/includes/deploy/snippet-create-bot-msa.md b/articles/includes/deploy/snippet-create-bot-msa.md deleted file mode 100644 index d72308398..000000000 --- a/articles/includes/deploy/snippet-create-bot-msa.md +++ /dev/null @@ -1,16 +0,0 @@ -1. Go to the [**Application Registration Portal**](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade). -1. Click on **Add an app** to register your application, create **Application Id**, and **Generate New Password**. If you already have an application and password but don't remember the password, you will have to generate a new password in the Application secrets section. -1. Save both application ID and the new password you just generated, so you that can use them with the `az bot create` command. - -```cmd -az bot create --kind webapp --name --location --version v4 --lang --verbose --resource-group --appid "" --password "" --verbose -``` - -| Option | Description | -|:---|:---| -| --name | A unique name that is used to deploy the bot in Azure. It could be the same name as your local bot. DO NOT include spaces or underscores in the name. | -| --location | Geographic location used to create the bot service resources. For example, `eastus`, `westus`, `westus2`, and so on. | -| --lang | The language to use to create the bot: `Csharp`, or `Node`; default is `Csharp`. | -| --resource-group | Name of resource group in which to create the bot. You can configure the default group using `az configure --defaults group=`. | -| --appid | The Microsoft account ID (MSA ID) to be used with the bot. | -| --password | The Microsoft account (MSA) password for the bot. | diff --git a/articles/includes/deploy/snippet-create-bot.md b/articles/includes/deploy/snippet-create-bot.md deleted file mode 100644 index 77ebdd181..000000000 --- a/articles/includes/deploy/snippet-create-bot.md +++ /dev/null @@ -1,10 +0,0 @@ -```cmd -az bot create --kind webapp --name --location --version v4 --lang --verbose --resource-group -``` - -| Option | Description | -|:---|:---| -| --name | A unique name that is used to deploy the bot in Azure. It could be the same name as your local bot. DO NOT include spaces or underscores in the name. | -| --location | Geographic location used to create the bot service resources. For example, `eastus`, `westus`, `westus2`, and so on. | -| --lang | The language to use to create the bot: `Csharp`, or `Node`; default is `Csharp`. | -| --resource-group | Name of resource group in which to create the bot. You can configure the default group using `az configure --defaults group=`. | \ No newline at end of file diff --git a/articles/includes/deploy/snippet-create-web-app.md b/articles/includes/deploy/snippet-create-web-app.md deleted file mode 100644 index 4ab0a7e58..000000000 --- a/articles/includes/deploy/snippet-create-web-app.md +++ /dev/null @@ -1,5 +0,0 @@ -Then, create the bot resource into which you will publish your bot. This will provision the necessary resources in Azure and create a bot web app, which you will overwrite with your local bot. - -> [!NOTE] -> This isn't the only way to achieve this, but it tends to be the easiest way to create all the parts you will need for deploying a functioning bot. - diff --git a/articles/includes/deploy/snippet-decrypt-bot.md b/articles/includes/deploy/snippet-decrypt-bot.md deleted file mode 100644 index be4de19bf..000000000 --- a/articles/includes/deploy/snippet-decrypt-bot.md +++ /dev/null @@ -1,32 +0,0 @@ -Get the encryption key. - -1. Log into the [Azure portal](http://portal.azure.com/). -1. Open the Web App Bot resource for your bot. -1. Open the bot's **Application Settings**. -1. In the **Application Settings** window, scroll down to the **Application settings**. -1. Locate the **botFileSecret** and copy its value. - -Decrypt the .bot file. - -```cmd -msbot secret --bot --secret "" --clear -``` - -| Option | Description | -|:---|:---| -| --bot | The relative path to the downloaded .bot file. | -| --secret | The encryption key. | - -Copy the decrypted `.bot` file to the directory that contains your local bot project, update your bot to use this new `.bot` file, and remove your old `.bot` file. - -# [C#](#tab/csharp) - -In **appsettings.json**, update the **botFilePath** property to point to the new `.bot` file you've added to your local directory. - -# [JavaScript](#tab/javascript) - -In **.env**, update the **botFilePath** property to point to the new `.bot` file you've added to your local directory. - ---- - -Once your bot has been updated, delete the temporary directory of the downloaded bot. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-deploy-code-to-az.md b/articles/includes/deploy/snippet-deploy-code-to-az.md deleted file mode 100644 index 44450139b..000000000 --- a/articles/includes/deploy/snippet-deploy-code-to-az.md +++ /dev/null @@ -1,15 +0,0 @@ -At this point we are ready to deploy the code to the Azure Web App. Run the following command from the command line to perform deployment using the kudu zip push deployment for a web app. - -```cmd -az webapp deployment source config-zip --resource-group "" --name "" --src -``` - -| Option | Description | -|:---------|:------------| -| resource-group | The name of the Azure resource group that contains your bot. (This will be the resource group you used or created when creating the app registration for your bot.) | -| name | Name of the Web App you used earlier. | -| src | The path to the zipped project file you created. | - -> [!NOTE] -> This step can take a few minutes to complete. -> Also it can take a few more minutes between when the deployment finishes and when your bot is available to test. \ No newline at end of file diff --git a/articles/includes/deploy/snippet-download-bot.md b/articles/includes/deploy/snippet-download-bot.md deleted file mode 100644 index 3272f6f6a..000000000 --- a/articles/includes/deploy/snippet-download-bot.md +++ /dev/null @@ -1,13 +0,0 @@ -Use a temporary directory outside of your current project directory. - -This command will create a subdirectory under the save-path; however, the specified path must already exist. - -```cmd -az bot download --name --resource-group --save-path "" -``` - -| Option | Description | -|:---|:---| -| --name | The name of the bot in Azure. | -| --resource-group | Name of resource group the bot is in. | -| --save-path | An existing directory to download bot code to. | \ No newline at end of file diff --git a/articles/includes/deploy/snippet-prepare-deploy-intro.md b/articles/includes/deploy/snippet-prepare-deploy-intro.md index 752681427..8743991b9 100644 --- a/articles/includes/deploy/snippet-prepare-deploy-intro.md +++ b/articles/includes/deploy/snippet-prepare-deploy-intro.md @@ -1,12 +1,25 @@ -When you create a bot using the [Visual Studio template](https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-sdk-quickstart?view=azure-bot-service-4.0), [Yeoman template](https://docs.microsoft.com/azure/bot-service/javascript/bot-builder-javascript-quickstart?view=azure-bot-service-4.0), or [Cookiecutter template](https://docs.microsoft.com/azure/bot-service/python/bot-builder-python-quickstart?view=azure-bot-service-4.0) the source code generated includes a `deploymentTemplates` folder that contains ARM templates. The deployment process documented here uses one of the ARM templates to provision required resources for the bot in Azure by using the Azure CLI. + -> [!NOTE] -> With the release of Bot Framework SDK 4.3, we have _deprecated_ the use of a .bot file. Instead, we use an appsettings.json or .env file to manage bot resources. For information on migrating settings from the .bot file to appsettings.json or .env file, see [managing bot resources](https://docs.microsoft.com/azure/bot-service/bot-file-basics?view=azure-bot-service-4.0). +# [C#](#tab/csharp) -### Bot ready to deploy +If you're deploying a C#, bot make sure that it has been [built in Release mode](/visualstudio/debugger/how-to-set-debug-and-release-configurations). In Visual Studio, make sure that the solution configuration is set to **Release** and perform a clean rebuild of the solution before continuing. The deployment may fail if the solution configuration is set to **Debug**. -This article assumes that you have a bot ready to be deployed and the **path** to the related project. You need the path to access the deployment templates and also to create a *zip* file to deploy. +When you [Create a bot](../../bot-service-quickstart-create-bot.md), the source code generated includes a `DeploymentTemplates` folder that contains ARM templates. The deployment process documented here uses one of the ARM templates to provision required resources for the bot in Azure by using the Azure CLI. -For information on how to create a simple echo bot, see the quick start [CSharp sample](~/dotnet/bot-builder-dotnet-sdk-quickstart.md) or [JavaScript sample](~/javascript/bot-builder-javascript-quickstart.md) or [Python sample](~/python/bot-builder-python-quickstart.md). +[!INCLUDE [about .NET Core versions in the templates](../vsix-templates-versions.md)] -You can also use one of the samples provided in the [Bot Framework Samples](https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md) repository. \ No newline at end of file +# [JavaScript / TypeScript](#tab/javascript+typescript) + +When you create a bot using a [Yeoman template](../../javascript/bot-builder-javascript-quickstart.md), the source code generated includes a `deploymentTemplates` folder that contains ARM templates. The deployment process documented here uses one of the ARM templates to provision required resources for the bot in Azure by using the Azure CLI. + +# [Java](#tab/java) + +When you create a bot using a [Yeoman template](../../java/bot-builder-java-quickstart.md), the source code generated includes a `deploymentTemplates` folder that contains ARM templates. The deployment process documented here uses one of the ARM templates to provision required resources for the bot in Azure by using the Azure CLI. + +# [Python](#tab/python) + +When you create a bot using a [Cookiecutter template](../../python/bot-builder-python-quickstart.md), the source code generated includes a `deploymentTemplates` folder that contains ARM templates. The deployment process documented here uses one of the ARM templates to provision required resources for the bot in Azure by using the Azure CLI. + +--- diff --git a/articles/includes/deploy/snippet-prerequisite.md b/articles/includes/deploy/snippet-prerequisite.md deleted file mode 100644 index 18e9adaae..000000000 --- a/articles/includes/deploy/snippet-prerequisite.md +++ /dev/null @@ -1,17 +0,0 @@ -- A subscription to [Microsoft Azure](https://azure.microsoft.com/free/) -- A C#, JavaScript, TypeScript, or Python bot that you have developed on your local machine -- Latest version of the [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) -- Familiarity with [Azure CLI and ARM templates](https://docs.microsoft.com/azure/azure-resource-manager/resource-group-overview) - - diff --git a/articles/includes/deploy/snippet-publish-js.md b/articles/includes/deploy/snippet-publish-js.md deleted file mode 100644 index 75a27aadd..000000000 --- a/articles/includes/deploy/snippet-publish-js.md +++ /dev/null @@ -1,17 +0,0 @@ -To publish your local JavaScript bot back to Azure, you must first manually create a single zipped file containing all files used to locally build and run your bot. This includes all npm libraries downloaded into your `node_modules` folder. When creating this zip file _make sure that the root directory you use is the same directory where your index.js file resides_. - -Once a zip file containing all of your bot's source code has been created, open a command prompt window and run the following _Az cli_ command. - -This step might take a while. - -```cmd -az webapp deployment source config-zip --resource-group --name --src -``` - -| Option | Description | -|:---|:---| -| --resource-group | Name of resource group in Azure. | -| --name | The resource name of the bot in Azure. | -| --src | The full directory path to upload your zipped bot code from. For example `c:\my-local-repository\this-app-folder\my-zipped-code.zip` | - -Once this completes successfully, your bot is deployed in Azure. diff --git a/articles/includes/deploy/snippet-publish.md b/articles/includes/deploy/snippet-publish.md deleted file mode 100644 index 0acc56110..000000000 --- a/articles/includes/deploy/snippet-publish.md +++ /dev/null @@ -1,14 +0,0 @@ -Publish your local bot to Azure. This step might take a while. - -```cmd -az bot publish --name --proj-file-path "" --resource-group --code-dir --verbose --version v4 -``` - -| Option | Description | -|:---|:---| -| --name | The resource name of the bot in Azure. | -| --proj-file-path | For C#, use the startup project file name (without the .csproj) that needs to be published. For example: `EnterpriseBot`. For Node.js, use the main entry point for the bot. For example, `index.js`. | -| --resource-group | Name of resource group. | -| --code-dir | The directory to upload bot code from. | - -Once this completes with a "Deployment successful!" message, your bot is deployed in Azure. diff --git a/articles/includes/deploy/snippet-test-in-web-chat.md b/articles/includes/deploy/snippet-test-in-web-chat.md index 6d8917d63..6686734ae 100644 --- a/articles/includes/deploy/snippet-test-in-web-chat.md +++ b/articles/includes/deploy/snippet-test-in-web-chat.md @@ -1,9 +1,6 @@ 1. In your browser, navigate to the [Azure portal](https://ms.portal.azure.com). -2. In the left panel, click **Resource groups**. -3. In the right panel, search for your group. -4. Click on your group name. -5. Click the link of your Bot Channels Registration. -6. In the **Bot Channels Registration** panel, click **Test in Web Chat**. -Alternatively, in the right panel, click the **Test** box. +1. Go to your bot resource. +1. Open the **Test in Web Chat** pane. +1. Interact with your deployed bot. -For more information about bot channels registration, see [Register a bot with Bot Service](https://docs.microsoft.com/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0). +For more information about bot registration, see [Register a bot with Bot Service](../../bot-service-quickstart-registration.md). diff --git a/articles/includes/deploy/snippet-zip-code.md b/articles/includes/deploy/snippet-zip-code.md deleted file mode 100644 index 5f36041cd..000000000 --- a/articles/includes/deploy/snippet-zip-code.md +++ /dev/null @@ -1,15 +0,0 @@ -When using the non-configured [zip deploy API](https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file-or-url) to deploy your bot's code, Web App/Kudu's behavior is as follows: - -_Kudu assumes by default that deployments from zip files are ready to run and do not require additional build steps during deployment, such as npm install or dotnet restore/dotnet publish._ - -It is important to include your built code with all necessary dependencies in the zip file being deployed, otherwise your bot will not work as intended. - -> [!IMPORTANT] -> Before zipping your project files, make sure that you are _in_ the project folder. -> - For C# bots, it is the folder that has the .csproj file. -> - For JavaScript bots, it is the folder that has the app.js or index.js file. -> - For TypeScript bots, it is the folder that includes the _src_ folder (where the bot.ts and index.ts files are). -> - For Python bots, it is the folder that has the app.py file. - ->**Within** the project folder, select all the files and folders you want included in your zip file before running the command to create the zip file, this will create a single zip file containing all selected files and folders. -> If your root folder location is incorrect, the **bot will fail to run in the Azure portal**. diff --git a/articles/includes/deployment-note-cli.md b/articles/includes/deployment-note-cli.md deleted file mode 100644 index 47dbcdccc..000000000 --- a/articles/includes/deployment-note-cli.md +++ /dev/null @@ -1,15 +0,0 @@ -If you are using services such as LUIS, you will also need to pass `luisAuthoringKey`. If you want to use existing resource group in Azure, use the `groupName` argument with the above command. - -It is highly recommended that you use the `verbose` option to help troubleshoot problems that might occur during the deployment of the bot. Additional options used with the `msbot clone services` command are described below: - -| Arguments | Description | -|--------------|-------------| -| `folder` | Location of the `bot.recipe` file. By default the recipe file is created in the `DeploymentsScript/MSBotClone`. DO NOT MODIFY this file.| -| `location` | Geographic location used to create the bot service resources. For example, eastus, westus, westus2 etc.| -| `proj-file` | For C# bot it is the .csproj file. For JS bot it is the startup project file name (e.g. index.js) of your local bot.| -| `name` | A unique name that is used to deploy the bot in Azure. It could be the same name as your local bot. DO NOT include spaces or underscores in the name.| -| `luisAuthoringKey` | Your authoring key for the appropriate LUIS authoring region for the LUIS resources. | - -Before Azure resources can be created, you'll be prompted to complete authentication. Follow the instructions that appear on the screen to complete this step. - -Note that the above step takes _few seconds to minutes_ to complete, and the resource that are created in Azure have their names mangled. To learn more about name mangling, see [issue# 796](https://github.com/Microsoft/botbuilder-tools/issues/796) in the GitHub repo. diff --git a/articles/includes/directline-confirm-extension-bot-config.md b/articles/includes/directline-confirm-extension-bot-config.md new file mode 100644 index 000000000..958252ba3 --- /dev/null +++ b/articles/includes/directline-confirm-extension-bot-config.md @@ -0,0 +1,22 @@ +--- +description: Confirm Direct Line App Service extension and the bot are configured +author: emgrol +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +In your browser, go to `https://.azurewebsites.net/.bot`. If everything is correct, the page will return the following JSON content: + +```json + {"v":"123","k":true,"ib":true,"ob":true,"initialized":true} +``` + +- **v** shows the build version of the Direct Line App Service extension. +- **k** indicates whether the extension was able to read an extension key from its configuration. +- **initialized** indicates whether the extension was able to download bot metadata from Azure AI Bot Service. +- **ib** indicates whether the extension was able to establish an inbound connection to the bot. +- **ob** indicates whether the extension was able to establish an outbound connection from the bot. diff --git a/articles/includes/directline-enable-dl-asp.md b/articles/includes/directline-enable-dl-asp.md new file mode 100644 index 000000000..7553c2b86 --- /dev/null +++ b/articles/includes/directline-enable-dl-asp.md @@ -0,0 +1,39 @@ +--- +description: Enable bot Direct Line App Service extension +author: emgrol +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +1. In the Azure portal, go to your **Azure Bot** resource. + 1. Under **Settings** select **Channels** to configure the channels your bot accepts messages from. + 1. If it isn't already enabled, select the **Direct Line** channel from the list of **Available channels** to enable the channel. + 1. After enabling **Direct Line**, select it again from the **Channels** page. + 1. Select the **App Service extension** tab. + 1. Under **App Service Extension Keys**, select the eye icon next to the corresponding key. +1. Go to the home page and select **App Services** at the top of the page. Alternatively, display the portal menu and then select the **App Services** menu item. Azure will display the **App Services** page. +1. In the search box, enter your **Azure Bot** resource name. Your resource will be listed. + + Notice that if you hover over the icon or the menu item, you get a list of your last viewed resources. Your **Azure Bot** resource will likely be listed. + +1. Select your resource link. + 1. In the **Settings** section, select the **Configuration** menu item. + 1. In the right panel, add the following settings: + + |Name|Value| + |---|---| + |DirectLineExtensionKey|The value of the App Service extension key you copied earlier.| + |DIRECTLINE_EXTENSION_VERSION|latest| + + 1. If your bot's hosted in a sovereign or otherwise restricted Azure cloud, where you don't access Azure via the [public portal](https://portal.azure.com), you'll also need to add the following setting: + + |Name|Value| + |---|---| + |DirectLineExtensionABSEndpoint|The endpoint specific to the Azure cloud your bot is hosted in. For the USGov cloud for example, the endpoint is `https://directline.botframework.azure.us/v3/extension`.| + + 1. From within the **Configuration** section, select the **General** settings section and turn on **Web sockets**. + 1. Select **Save** to save the settings. This restarts the Azure App Service. diff --git a/articles/includes/directline-troubleshoot.md b/articles/includes/directline-troubleshoot.md new file mode 100644 index 000000000..b39984f3d --- /dev/null +++ b/articles/includes/directline-troubleshoot.md @@ -0,0 +1,19 @@ +--- +description: Troubleshoot Direct Line extension +author: emgrol +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +- If the **ib** and **ob** values displayed by the **.bot endpoint** are false, the bot and the Direct Line App Service extension are unable to connect to each other. + 1. Double check the code for using named pipes has been added to the bot. + 1. Confirm the bot is able to start up and run. Useful tools are **Test in WebChat**, connecting an additional channel, remote debugging, or logging. + 1. Restart the entire **Azure App Service** the bot is hosted within, to ensure a clean start up of all processes. + +- If the **initialized** value of the **.bot endpoint** is false, the Direct Line App Service extension is unable to validate the App Service extension key added to the bot's **Application Settings** above. + 1. Confirm the value was correctly entered. + 1. Switch to the alternate extension key shown on your bot's **Configure Direct Line** page. diff --git a/articles/includes/java-python-sunset-alert.md b/articles/includes/java-python-sunset-alert.md new file mode 100644 index 000000000..f704d0753 --- /dev/null +++ b/articles/includes/java-python-sunset-alert.md @@ -0,0 +1,13 @@ +--- +description: Sunset notice for the Bot Framework Java SDK +author: iaanw +ms.author: iawilt +manager: leeclontz +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +> [!NOTE] +> To build agents with your choice of AI services, orchestration, and knowledge, consider using the Microsoft 365 Agents SDK. The Agents SDK has support for C#, JavaScript or Python. You can learn more about the Agents SDK at [aka.ms/agents](https://github.com/Microsoft/Agents). If you're looking for a SaaS-based agent platform, consider [Microsoft Copilot Studio](https://www.microsoft.com/microsoft-copilot/microsoft-copilot-studio?msockid=357f4ad089cb66b636e85f308872673e). If you have an existing bot built with the Bot Framework SDK, you can update your bot to the Agents SDK. You can review the core changes and updates at [Bot Framework SDK to Agents SDK migration guidance](https://aka.ms/bfmigrationguidance). Support tickets for the Bot Framework SDK will no longer be serviced as of December 31, 2025. diff --git a/articles/includes/luis-sunset-alert.md b/articles/includes/luis-sunset-alert.md new file mode 100644 index 000000000..7ca2d0cfb --- /dev/null +++ b/articles/includes/luis-sunset-alert.md @@ -0,0 +1,18 @@ +--- +description: Sunset notice for the Language Understanding (LUIS) service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +> [!NOTE] +> [Language Understanding (LUIS) will be retired on 1 October 2025](https://azure.microsoft.com/updates/language-understanding-retirement/). +> Beginning 1 April 2023, you won't be able to create new LUIS resources. +> A newer version of language understanding is now available as part of Azure AI Language. +> +> Conversational language understanding (CLU), a feature of Azure AI Language, is the updated version of LUIS. +> For more information about language understanding support in the Bot Framework SDK, see [Natural language understanding](../v4sdk/bot-builder-concept-luis.md). diff --git a/articles/includes/pre-release-label-v3.md b/articles/includes/pre-release-label-v3.md deleted file mode 100644 index 35d50d50c..000000000 --- a/articles/includes/pre-release-label-v3.md +++ /dev/null @@ -1,2 +0,0 @@ -> [!NOTE] -> This topic applies to SDK v3 release. You can find the documentation for the latest version of the SDK v4 [here.](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) diff --git a/articles/includes/pre-release-label.md b/articles/includes/pre-release-label.md deleted file mode 100644 index 612139215..000000000 --- a/articles/includes/pre-release-label.md +++ /dev/null @@ -1,2 +0,0 @@ -> [!NOTE] -> This topic is for the latest release of the SDK (v4). You can find content for the older version of the SDK (v3) [here.](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-3.0) diff --git a/articles/includes/qnamaker-sunset-alert.md b/articles/includes/qnamaker-sunset-alert.md new file mode 100644 index 000000000..d6328bb71 --- /dev/null +++ b/articles/includes/qnamaker-sunset-alert.md @@ -0,0 +1,18 @@ +--- +description: Sunset notice for the QnA Maker service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +> [!NOTE] +> [Azure AI QnA Maker will be retired on 31 March 2025](https://azure.microsoft.com/updates/azure-qna-maker-will-be-retired-on-31-march-2025/). +> Beginning 1 October 2022, you won't be able to create new QnA Maker resources or knowledge bases. +> A newer version of the question and answering capability is now available as part of Azure AI Language. +> +> Custom question answering, a feature of Azure AI Language, is the updated version of the QnA Maker service. +> For more information about question-and-answer support in the Bot Framework SDK, see [Natural language understanding](../v4sdk/bot-builder-concept-luis.md). diff --git a/articles/includes/quickstart-dotnet.md b/articles/includes/quickstart-dotnet.md deleted file mode 100644 index 2220058cc..000000000 --- a/articles/includes/quickstart-dotnet.md +++ /dev/null @@ -1,38 +0,0 @@ -## Prerequisites -- [Visual Studio 2017 or later](https://www.visualstudio.com/downloads) -- [Bot Framework SDK v4 template for C#](https://aka.ms/bot-vsix) -- [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) -- Knowledge of [ASP.Net Core](https://docs.microsoft.com/aspnet/core/) and [asynchronous programming in C#](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/index) - -## Create a bot -Install [BotBuilderVSIX.vsix template](https://aka.ms/bot-vsix) that you downloaded in the prerequisites section. - -In Visual Studio, create a new bot project using the **Echo Bot (Bot Framework v4)** template. Enter _bot framework v4_ in the search box to show only bot templates. - -![Visual Studio create a new project dialog](../media/azure-bot-quickstarts/bot-builder-dotnet-project-vs2019.png) - -> [!TIP] -> If using Visual Studio 2017, make sure that the project build type is ``.Net Core 2.1`` or later. Also if needed, update the `Microsoft.Bot.Builder` [NuGet packages](https://docs.microsoft.com/nuget/quickstart/install-and-use-a-package-in-visual-studio). - -Thanks to the template, your project contains all the code that's necessary to create the bot in this quickstart. You won't actually need to write any additional code. - -## Start your bot in Visual Studio - -When you click the run button, Visual Studio will build the application, deploy it to localhost, and launch the web browser to display the application's `default.htm` page. At this point, your bot is running locally. - -## Start the emulator and connect your bot - -Next, start the emulator and then connect to your bot in the emulator: - -1. Click the **Create a new bot configuration** link in the emulator "Welcome" tab. -2. Fill out the fields for your bot. Use your bot's welcome page address (typically http://localhost:3978) and append routing info '/api/messages' to this address. -3. then click **Save and connect**. - -## Interact with your bot - -Send a message to your bot, and the bot will respond back with a message. - -![Emulator running](~/media/emulator-v4/emulator-running.png) - -> [!NOTE] -> If you see that the message cannot be sent, you might need to restart your machine as ngrok didn't get the needed privileges on your system yet (only needs to be done one time). diff --git a/articles/includes/quickstart-javascript.md b/articles/includes/quickstart-javascript.md deleted file mode 100644 index 12662bfec..000000000 --- a/articles/includes/quickstart-javascript.md +++ /dev/null @@ -1,75 +0,0 @@ -## Prerequisites - -- [Visual Studio Code](https://www.visualstudio.com/downloads) -- [Node.js](https://nodejs.org/) -- [Yeoman](http://yeoman.io/), which uses a generator to create a bot for you -- [git](https://git-scm.com/) -- [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) -- Knowledge of [restify](http://restify.com/) and asynchronous programming in JavaScript - -> [!NOTE] -> The install of Windows build tools listed below is only required if you use Windows as your development operating system. -> For some installations the install step for restify is giving an error related to node-gyp. -> If this is the case you can try running this command with elevated permissions. -> This call may also hang without exiting if python is already installed on your system: - -> ```bash -> # only run this command if you are on Windows. Read the above note. -> npm install -g windows-build-tools -> ``` - -## Create a bot - -To create your bot and initialize its packages - -1. Open a terminal or elevated command prompt. -1. If you don't already have a directory for your JavaScript bots, create one and change directories to it. (We're creating a directory for your JavaScript bots in general, even though we're only creating one bot in this tutorial.) - - ```bash - mkdir myJsBots - cd myJsBots - ``` - -1. Ensure your version of npm is up to date. - - ```bash - npm install -g npm - ``` - -1. Next, install Yeoman and the generator for JavaScript. - - ```bash - npm install -g yo generator-botbuilder - ``` - -1. Then, use the generator to create an echo bot. - - ```bash - yo botbuilder - ``` - -Yeoman prompts you for some information with which to create your bot. For this tutorial, use the default values. - -- Enter a name for your bot. (my-chat-bot) -- Enter a description. (Demonstrate the core capabilities of the Microsoft Bot Framework) -- Choose the language for your bot. (JavaScript) -- Choose the template to use. (Echo Bot - https://aka.ms/generator-botbuilder-templates) - -Thanks to the template, your project contains all the code that's necessary to create the bot in this quickstart. You won't actually need to write any additional code. - -> [!NOTE] -> If you choose to create a `Core` bot, you'll need a LUIS language model. You can create one on [luis.ai](https://www.luis.ai). After creating the model, update the configuration file. - -## Start your bot - -In a terminal or command prompt change directories to the one created for your bot, and start it with `npm start`. At this point, your bot is running locally. - -## Start the Emulator and connect your bot - -1. Start the Bot Framework Emulator. -2. Click the **Create a new bot configuration** link in the emulator "Welcome" tab. -3. Fill out the fields for your bot. Use your bot's welcome page address (typically http://localhost:3978) and append routing info '/api/messages' to this address. -4. Then click **Save and connect**. - -Send a message to your bot, and the bot will respond back with a message. -![Emulator running](../media/emulator-v4/js-quickstart.png) diff --git a/articles/includes/quickstart-python.md b/articles/includes/quickstart-python.md deleted file mode 100644 index 15878fad7..000000000 --- a/articles/includes/quickstart-python.md +++ /dev/null @@ -1,46 +0,0 @@ -## Prerequisites -- Python [3.6](https://www.python.org/downloads/release/python-369/) or [3.7](https://www.python.org/downloads/release/python-375/) -- [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) -- [git](https://git-scm.com/) -- knowledge of ansynchronous programming in Python - -## Create a bot -1. Open a terminal and navigate to the folder where you're saving your bot locally. Install the necessary packages by running the following commands: -- `pip install botbuilder-core` -- `pip install asyncio` -- `pip install -r requirements.txt` -- `pip install cookiecutter` - -The last package, cookiecutter, will be used to generate your bot. Verify that cookiecutter was installed correctly by running `cookiecutter --help`. - -2. To create your bot run: - -```cmd -cookiecutter https://github.com/microsoft/botbuilder-python/releases/download/Templates/echo.zip -``` - -This command creates an Echo Bot based on the Python [echo template](https://github.com/microsoft/botbuilder-python/tree/master/generators/app/templates/echo). - -3. You will then be prompted for the *name* of the bot and a *description*. Name your bot `echo-bot` and set the description to `A bot that echoes back user response.` as shown below: - -![set name and description](~/media/python/quickstart/set-name-description.png) - -Copy the last for digits in the address on the last line (usually 3978) since you will be using them in the next step. You are now ready to start your bot. - -## Start you bot -1. From a terminal navigate to the `echo-bot` folder where you saved your bot. Run `pip install -r requirements.txt` to install any required packages to run your bot. - -2. Once the packages are installted run `python app.py` to start your bot. You will know your bot is ready to test when you see the last line shown in the screenshot below: - -![bot running locally](~/media/python/quickstart/bot-running-locally.png) - -## Start the Emulator and connect your bot -1. Start the Emulator and click the **Open Bot** button. - -2. After clicking the button a box window will open where you set the necessary values to run the bot. Use the number you saved earlier and set the **Bot URL** to `http://localhost:/api/messages` as seen below: - -![open a bot screen](~/media/python/quickstart/open-bot.png) - -3. Click the **Connect** button and your bot should start. Try testing the bot by typing anything and clicking *Enter* as seen below: - -![connect and test](~/media/python/quickstart/connect-and-start.png) diff --git a/articles/includes/quickstart/dotnet/add-templates.md b/articles/includes/quickstart/dotnet/add-templates.md new file mode 100644 index 000000000..5e94cd2a5 --- /dev/null +++ b/articles/includes/quickstart/dotnet/add-templates.md @@ -0,0 +1,54 @@ +--- +description: Procedure for getting C# bot templates by various means, part of the quickstart to create a basic bot. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +#### [Visual Studio](#tab/vs) + +- [Visual Studio 2022 or later](https://www.visualstudio.com/downloads) +- [Bot Framework v4 SDK Templates for Visual Studio](https://marketplace.visualstudio.com/items?itemName=BotBuilder.botbuilderv4) + +To add the bot templates to Visual Studio, download and install the [Bot Framework v4 SDK Templates for Visual Studio](https://marketplace.visualstudio.com/items?itemName=BotBuilder.botbuilderv4) VSIX file. + +[!INCLUDE [Install VSIX templates from within Visual Studio](../../vsix-templates-versions.md)] + +#### [VS Code / CLI](#tab/vscode+cli) + +.NET Core Templates will help you to quickly build new conversational AI bots using Bot Framework v4. +As of May 2020, these templates and the code they generate require .NET Core 3.1 or later. + +To install the Bot Framework templates: + +1. Open a console window. + +1. Download and install [.NET Core SDK download](https://dotnet.microsoft.com/download) version 3.1 or later. +1. You can use this command to determine which versions of the .NET Core command-line interface are installed. + + ```console + dotnet --version + ``` + +1. Install the three Bot Framework C# templates: the echo, core, and empty bot templates. + + ```console + dotnet new -i Microsoft.Bot.Framework.CSharp.EchoBot + dotnet new -i Microsoft.Bot.Framework.CSharp.CoreBot + dotnet new -i Microsoft.Bot.Framework.CSharp.EmptyBot + ``` + +1. Verify the templates have been installed correctly. + + ```console + dotnet new --list + ``` + +> [!NOTE] +> The steps above install all three Bot Framework templates. You don't need to install all the templates and can install just the ones you'll use. This article makes use of the _echo bot_ template. + +--- diff --git a/articles/includes/quickstart/dotnet/create-bot.md b/articles/includes/quickstart/dotnet/create-bot.md new file mode 100644 index 000000000..4866d578c --- /dev/null +++ b/articles/includes/quickstart/dotnet/create-bot.md @@ -0,0 +1,40 @@ +--- +description: Procedure for getting C# bot templates by various means, part of the quickstart to create a basic bot. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +#### [Visual Studio](#tab/vs) + +In Visual Studio, create a new bot project and use the **Echo Bot (Bot Framework v4 - .NET Core 3.1)** template. To see only bot templates, choose the **AI Bots** project type. + +#### [VS Code](#tab/vscode) + +Make sure that [.NET Core 3.1](https://dotnet.microsoft.com/download) or later is installed. + +1. In Visual Studio Code, open a new terminal window. +1. Go to the directory in which you want to create your bot project. +1. Create a new echo bot project using the following command. Replace `` with the name to use for your bot project. + + ```console + dotnet new echobot -n + ``` + +#### [CLI](#tab/cli) + +1. Open a new terminal window. +1. Go to the directory in which you want to create your bot project. +1. Create a new echo bot project using the following command. Replace `` with the name to use for your bot project. + + ```console + dotnet new echobot -n + ``` + +--- + +Thanks to the template, your project contains all the necessary code to create the bot in this quickstart. You don't need any more code to test your bot. diff --git a/articles/includes/quickstart/dotnet/start-bot.md b/articles/includes/quickstart/dotnet/start-bot.md new file mode 100644 index 000000000..36fb0b374 --- /dev/null +++ b/articles/includes/quickstart/dotnet/start-bot.md @@ -0,0 +1,54 @@ +--- +description: Procedure for getting C# bot templates by various means, part of the quickstart to create a basic bot. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +#### [Visual Studio](#tab/vs) + +In Visual Studio: + +1. Open your bot project. +1. Run the project without debugging. +1. Visual Studio builds the application, deploys it to localhost, and launches the web browser to display the application's `default.htm` page. + +At this point, your bot is running locally on port 3978. + +#### [VS Code](#tab/vscode) + +In Visual Studio Code: + +1. Open your bot project folder. + + If you're prompted to select a project, select the one for the bot you created. + +1. From the menu, select **Run**, and then **Run Without Debugging**. + + - If prompted to select an environment, select **.Net Core**. + - If this command updated your launch settings, save the changes and rerun the command. + + The run command builds the application, deploys it to localhost, and launches the web browser to display the application's `default.htm` page. + +At this point, your bot is running locally on port 3978. + +#### [CLI](#tab/cli) + +From a command prompt or terminal: + +1. Change directories to the project folder for your bot. +1. Use `dotnet run` to start the bot. + + ```console + dotnet run + ``` + +1. This command builds the application and deploys it to localhost. + +The application's default web page won't display, but at this point, your bot is running locally on port 3978. + +--- diff --git a/articles/includes/ropc-connection-strings-important.md b/articles/includes/ropc-connection-strings-important.md new file mode 100644 index 000000000..855a4cf1b --- /dev/null +++ b/articles/includes/ropc-connection-strings-important.md @@ -0,0 +1,2 @@ +> [!IMPORTANT] +> This article contains legacy code samples using [connection strings](/dotnet/framework/data/adonet/connection-strings-and-configuration-files) in configuration files for internal connection to storage. Microsoft recommends that you use the most secure authentication flow available. If you're connecting to an Azure resource, [Managed Identities for Azure resources](/entra/identity/managed-identities-azure-resources) is the recommended authentication method. diff --git a/articles/includes/skills-about-debugging.md b/articles/includes/skills-about-debugging.md new file mode 100644 index 000000000..3359f21d5 --- /dev/null +++ b/articles/includes/skills-about-debugging.md @@ -0,0 +1,8 @@ +Since traffic between skills and skill consumers is authenticated, there are extra steps when debugging such bots. + +- The skill consumer and all the skills it consumes, directly or indirectly, must be running. +- If the bots are running locally and if any of the bots has an app ID and password, then all bots must have valid IDs and passwords. +- If the bots are all deployed, see how to [Debug a bot from any channel using devtunnel](../bot-service-debug-channel-devtunnel.md). +- If some of the bots are running locally and some are deployed, then see how to [Debug a skill or skill consumer](../v4sdk/skills-debug-skill-or-consumer.md). + +Otherwise, you can debug a skill consumer or skill much like you debug other bots. For more information, see [Debugging a bot](../bot-service-debug-bot.md) and [Debug with the Bot Framework Emulator](../bot-service-debug-emulator.md). diff --git a/articles/includes/skills-and-identity-types.md b/articles/includes/skills-and-identity-types.md new file mode 100644 index 000000000..2e5d8f8c9 --- /dev/null +++ b/articles/includes/skills-and-identity-types.md @@ -0,0 +1,19 @@ +--- +description: Table of support for different combinations of skill and consumer identity-management flavors. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: include +ms.custom: + - evergreen +--- + +Some types of skill consumers are not able to use some types of skill bots. +The following table describes which combinations are supported. + +|   | Multi-tenant skill | Single-tenant skill | User-assigned managed identity skill | +|:--------------------------------------------|:-------------------|:---------------------------------------------|:---------------------------------------------| +| **Multi-tenant consumer** | Supported | Not supported | Not supported | +| **Single-tenant consumer** | Not supported | Supported if both apps belong to same tenant | Supported if both apps belong to same tenant | +| **User-assigned managed identity consumer** | Not supported | Supported if both apps belong to same tenant | Supported if both apps belong to same tenant | diff --git a/articles/includes/snippet-abs-hosting-plans.md b/articles/includes/snippet-abs-hosting-plans.md deleted file mode 100644 index 16b0c51b2..000000000 --- a/articles/includes/snippet-abs-hosting-plans.md +++ /dev/null @@ -1,14 +0,0 @@ -Bot Service offers two different hosting plans for bots. Converting bot source code from one plan to the other is a manual process. - -## App Service plan - -A bot that uses an App Service plan is a standard Azure web app you can set to allocate a predefined capacity with predictable costs and scaling. With a bot that uses this hosting plan, you can: - -* Edit bot source code online using an advanced in-browser code editor. -* Download, debug, and re-publish your C# bot using Visual Studio. -* Set up continuous deployment easily for Visual Studio Online and Github. -* Use sample code prepared for the Bot Framework SDK. - -## Consumption plan - -A bot that uses a Consumption plan is a serverless bot that runs on Azure Functions, and uses the pay-per-run Azure Functions pricing. A bot that uses this hosting plan can scale to handle huge traffic spikes. You can edit bot source code online using a basic in-browser code editor. For more information about the runtime environment of a Consumption plan bot, see Azure Functions Consumption and App Service plans. diff --git a/articles/includes/snippet-abs-key-download.md b/articles/includes/snippet-abs-key-download.md deleted file mode 100644 index 6134ebe1d..000000000 --- a/articles/includes/snippet-abs-key-download.md +++ /dev/null @@ -1 +0,0 @@ -When downloading your bot, you will be given the option to include the settings (containing the keys and secrets) for your bot in your download, which may be necessary for your bot to work. If you choose **Yes**, the `appsettings.json` or `.env` file will have the keys. \ No newline at end of file diff --git a/articles/includes/snippet-abs-templates.md b/articles/includes/snippet-abs-templates.md deleted file mode 100644 index e68a3030e..000000000 --- a/articles/includes/snippet-abs-templates.md +++ /dev/null @@ -1,8 +0,0 @@ -| Template | Description | -|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Basic | Creates a bot that uses dialogs to respond to user input. | -| Form | Creates a bot that collects input from a user via a guided conversation that is created using [FormFlow](~/dotnet/bot-builder-dotnet-formflow.md) in C# or [waterfalls](~/nodejs/bot-builder-nodejs-prompts.md) in Node.js. | -| Language understanding | Creates a bot that uses natural language models (LUIS) to understand user intent. | -| QnA Maker | Creates a bot that uses the QnA Maker service to answer user's FAQs. | -| Proactive | Creates a bot that uses Azure Functions to alert users of events. | - diff --git a/articles/includes/snippet-audio-call-intro.md b/articles/includes/snippet-audio-call-intro.md deleted file mode 100644 index 68b5f4bda..000000000 --- a/articles/includes/snippet-audio-call-intro.md +++ /dev/null @@ -1,4 +0,0 @@ -If you are building a bot for Skype, your bot can communicate with users via audio call. -Audio calls are useful when the user does not want to or cannot provide input by typing, tapping, or clicking. - -A bot may support other user controls such as rich cards or text in addition to audio calls, or communicate through audio calls only. \ No newline at end of file diff --git a/articles/includes/snippet-backchannel.md b/articles/includes/snippet-backchannel.md deleted file mode 100644 index 679d2c9cd..000000000 --- a/articles/includes/snippet-backchannel.md +++ /dev/null @@ -1,7 +0,0 @@ -The open source web chat control -communicates with bots by using the [Direct Line API](https://docs.botframework.com/restapi/directline3/#navtitle), -which allows `activities` to be sent back and forth between client and bot. -The most common type of activity is `message`, but there are other types as well. -For example, the activity type `typing` indicates that a user is typing or that the bot is working to compile a response. - -You can use the backchannel mechanism to exchange information between client and bot without presenting it to the user by setting the activity type to `event`. The web chat control will automatically ignore any activities where `type="event"`. \ No newline at end of file diff --git a/articles/includes/snippet-card-action-types.md b/articles/includes/snippet-card-action-types.md new file mode 100644 index 000000000..3b48ecca5 --- /dev/null +++ b/articles/includes/snippet-card-action-types.md @@ -0,0 +1,16 @@ +To function correctly, assign an action type to each clickable item on a hero card. This table lists and describes the available action types and what should be in the associated value property. + +The `messageBack` card action has a more generalized meaning than the other card actions. See the [Card action](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#card-action) section of the [Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) for more information about the `messageBack` and other card action types. + +| Type | Description | Value | +|:-------------|:-----------------------------------------------------------------------------|:-------------------------------------------------------------------| +| call | Initiates a phone call. | Destination for the phone call in this format: `tel:123123123123`. | +| downloadFile | Downloads a file. | The URL of the file to download. | +| imBack | Sends a message to the bot, and posts a visible response in the chat. | Text of the message to send. | +| messageBack | Represents a text response to be sent via the chat system. | An optional programmatic value to include in generated messages. | +| openUrl | Opens a URL in the built-in browser. | The URL to open. | +| playAudio | Plays audio. | The URL of the audio to play. | +| playVideo | Plays a video. | The URL of video to play. | +| postBack | Sends a message to the bot, and may not post a visible response in the chat. | Text of the message to send. | +| showImage | Displays an image. | The URL of the image to display. | +| signin | Initiates an OAuth sign-in process. | The URL of the OAuth flow to initiate. | diff --git a/articles/includes/snippet-channel-inspector.md b/articles/includes/snippet-channel-inspector.md index a455166d0..007152e93 100644 --- a/articles/includes/snippet-channel-inspector.md +++ b/articles/includes/snippet-channel-inspector.md @@ -1,2 +1,2 @@ > [!TIP] -> For tables describing which features are supported on each channel, see the [channels reference](../bot-service-channels-reference.md) article. \ No newline at end of file +> For information about which features are supported on each channel, see the [channels reference](../bot-service-channels-reference.md) article. diff --git a/articles/includes/snippet-channelData-email.md b/articles/includes/snippet-channelData-email.md index 469133ee0..a418b6e84 100644 --- a/articles/includes/snippet-channelData-email.md +++ b/articles/includes/snippet-channelData-email.md @@ -1,8 +1,16 @@ -| Property | Description | -| :--- | :--- | -| htmlBody | The HTML to use for the body of the message. | -| subject | The subject to use for the message. | -| importance | The importance flag to use for the message: `low`, `normal`, or `high`. | -| toRecipients | A semicolon (;) delimited string of email addresses to add to the message's To field. | -| ccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Cc (carbon copy) field. | -| bccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Bcc (blind carbon copy) field. | +```json +{ + "type": "ActivityTypes.Message", + "locale": "en-Us", + "channelID": "email", + "fromName": { "id": "mybot@mydomain.com", "name": "My bot"}, + "recipientName": { "id": "joe@otherdomain.com", "name": "Joe Doe"}, + "conversation": { "id": "123123123123", "topic": "awesome chat" }, + "channelData": + { + "htmlBody": "This is more than awesome.", + "importance": "high", + "ccRecipients": "Yasemin@adatum.com;Temel@adventure-works.com", + } +} +``` diff --git a/articles/includes/snippet-channeldata.md b/articles/includes/snippet-channeldata.md deleted file mode 100644 index 8413f3dc6..000000000 --- a/articles/includes/snippet-channeldata.md +++ /dev/null @@ -1,505 +0,0 @@ -Some channels provide features that cannot be implemented by using only message text and attachments. To implement channel-specific functionality, you can pass native metadata to a channel in the activity object's _channel data_ property. For example, your bot can use the channel data property to instruct Telegram to send a sticker or to instruct Office365 to send an email. - -This article describes how to use a message activity's channel data property to implement this channel-specific functionality: - -| Channel | Functionality | -|----|----| -| Email | Send and receive an email that contains body, subject, and importance metadata | -| Slack | Send full fidelity Slack messages | -| Facebook | Send Facebook notifications natively | -| Telegram | Perform Telegram-specific actions, such as sharing a voice memo or a sticker | -| Kik | Send and receive native Kik messages | - -> [!NOTE] -> The value of an activity object's channel data property is a JSON object. -> Therefore, the examples in this article show the expected format of the -> `channelData` JSON property in various scenarios. -> To create a JSON object using .NET, use the `JObject` (.NET) class. - -## Create a custom Email message - -To create an email message, set the activity object's channel data property -to a JSON object that contains these properties: - -| Property | Description | -|----|----| -| bccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Bcc (blind carbon copy) field. | -| ccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Cc (carbon copy) field. | -| htmlBody | An HTML document that specifies the body of the email message. See the channel's documentation for information about supported HTML elements and attributes. | -| importance | The email's importance level. Valid values are **high**, **normal**, and **low**. The default value is **normal**. | -| subject | The email's subject. See the channel's documentation for information about field requirements. | -| toRecipients | A semicolon (;) delimited string of email addresses to add to the message's To field. | - -> [!NOTE] -> Messages that your bot receives from users via the Email channel may -> contain a channel data property that is populated with a JSON object like the one described above. - -This snippet shows an example of the `channelData` property for a custom email message. - -```json -"channelData": { - "type": "message", - "locale": "en-Us", - "channelID": "email", - "from": { "id": "mybot@mydomain.com", "name": "My bot"}, - "recipient": { "id": "joe@otherdomain.com", "name": "Joe Doe"}, - "conversation": { "id": "123123123123", "topic": "awesome chat" }, - "channelData": - { - "htmlBody": "This is more than awesome.", - "subject": "Super awesome message subject", - "importance": "high", - "ccRecipients": "Yasemin@adatum.com;Temel@adventure-works.com" - } -} -``` - -## Create a full-fidelity Slack message - -To create a full-fidelity Slack message, -set the activity object's channel data property to a JSON object that specifies -Slack messages, -Slack attachments, and/or -Slack buttons. - -> [!NOTE] -> To support buttons in Slack messages, you must enable **Interactive Messages** when you -> [connect your bot](../bot-service-manage-channels.md) to the Slack channel. - -This snippet shows an example of the `channelData` property for a custom Slack message. - -```json -"channelData": { - "text": "Now back in stock! :tada:", - "attachments": [ - { - "title": "The Further Adventures of Slackbot", - "author_name": "Stanford S. Strickland", - "author_icon": "https://api.slack.com/img/api/homepage_custom_integrations-2x.png", - "image_url": "http://i.imgur.com/OJkaVOI.jpg?1" - }, - { - "fields": [ - { - "title": "Volume", - "value": "1", - "short": true - }, - { - "title": "Issue", - "value": "3", - "short": true - } - ] - }, - { - "title": "Synopsis", - "text": "After @episod pushed exciting changes to a devious new branch back in Issue 1, Slackbot notifies @don about an unexpected deploy..." - }, - { - "fallback": "Would you recommend it to customers?", - "title": "Would you recommend it to customers?", - "callback_id": "comic_1234_xyz", - "color": "#3AA3E3", - "attachment_type": "default", - "actions": [ - { - "name": "recommend", - "text": "Recommend", - "type": "button", - "value": "recommend" - }, - { - "name": "no", - "text": "No", - "type": "button", - "value": "bad" - } - ] - } - ] -} -``` - -When a user clicks a button within a Slack message, your bot will receive a response message -in which the channel data property is populated with a `payload` JSON object. -The `payload` object specifies contents of the original message, -identifies the button that was clicked, and identifies the user who clicked the button. - -This snippet shows an example of the `channelData` property in the message that a bot receives -when a user clicks a button in the Slack message. - -```json -"channelData": { - "payload": { - "actions": [ - { - "name": "recommend", - "value": "yes" - } - ], - . . . - "original_message": "{…}", - "response_url": "https://hooks.slack.com/actions/..." - } -} -``` - -Your bot can reply to this message in the normal manner, -or it can post its response directly to the endpoint that is specified by -the `payload` object's `response_url` property. -For information about when and how to post a response to the `response_url`, see -Slack Buttons. - -You can create dynamic buttons using the following JSON. - -```json -{ - "text": "Would you like to play a game ? ", - "attachments": [ - { - "text": "Choose a game to play!", - "fallback": "You are unable to choose a game", - "callback_id": "wopr_game", - "color": "#3AA3E3", - "attachment_type": "default", - "actions": [ - { - "name": "game", - "text": "Chess", - "type": "button", - "value": "chess" - }, - { - "name": "game", - "text": "Falken's Maze", - "type": "button", - "value": "maze" - }, - { - "name": "game", - "text": "Thermonuclear War", - "style": "danger", - "type": "button", - "value": "war", - "confirm": { - "title": "Are you sure?", - "text": "Wouldn't you prefer a good game of chess?", - "ok_text": "Yes", - "dismiss_text": "No" - } - } - ] - } - ] -} -``` - -To create interactive menus, use the following JSON: - -```json -{ - "text": "Would you like to play a game ? ", - "response_type": "in_channel", - "attachments": [ - { - "text": "Choose a game to play", - "fallback": "If you could read this message, you'd be choosing something fun to do right now.", - "color": "#3AA3E3", - "attachment_type": "default", - "callback_id": "game_selection", - "actions": [ - { - "name": "games_list", - "text": "Pick a game...", - "type": "select", - "options": [ - { - "text": "Hearts", - "value": "menu_id_hearts" - }, - { - "text": "Bridge", - "value": "menu_id_bridge" - }, - { - "text": "Checkers", - "value": "menu_id_checkers" - }, - { - "text": "Chess", - "value": "menu_id_chess" - }, - { - "text": "Poker", - "value": "menu_id_poker" - }, - { - "text": "Falken's Maze", - "value": "menu_id_maze" - }, - { - "text": "Global Thermonuclear War", - "value": "menu_id_war" - } - ] - } - ] - } - ] -} -``` - -## Create a Facebook notification - -To create a Facebook notification, -set the activity object's channel data property to a JSON object that specifies these properties: - -| Property | Description | -|----|----| -| notification_type | The type of notification (e.g., **REGULAR**, **SILENT_PUSH**, **NO_PUSH**). -| attachment | An attachment that specifies an image, video, or other multimedia type, or a templated attachment such as a receipt. | - -> [!NOTE] -> For details about format and contents of the `notification_type` property and `attachment` property, see the -> Facebook API documentation. - -This snippet shows an example of the `channelData` property for a Facebook receipt attachment. - -```json -"channelData": { - "notification_type": "NO_PUSH", - "attachment": { - "type": "template" - "payload": { - "template_type": "receipt", - . . . - } - } -} -``` - -## Create a Telegram message - -To create a message that implements Telegram-specific actions, -such as sharing a voice memo or a sticker, -set the activity object's channel data property to a JSON object that specifies these properties: - -| Property | Description | -|----|----| -| method | The Telegram Bot API method to call. | -| parameters | The parameters of the specified method. | - -These Telegram methods are supported: - -- answerInlineQuery -- editMessageCaption -- editMessageReplyMarkup -- editMessageText -- forwardMessage -- kickChatMember -- sendAudio -- sendChatAction -- sendContact -- sendDocument -- sendLocation -- sendMessage -- sendPhoto -- sendSticker -- sendVenue -- sendVideo -- sendVoice -- unbanChateMember - -For details about these Telegram methods and their parameters, see the -Telegram Bot API documentation. - -> [!NOTE] ->
  • The chat_id parameter is common to all Telegram methods. If you do not specify chat_id as a parameter, the framework will provide the ID for you.
  • ->
  • Instead of passing file contents inline, specify the file using a URL and media type as shown in the example below.
  • ->
  • Within each message that your bot receives from the Telegram channel, the ChannelData property will include the message that your bot sent previously.
- -This snippet shows an example of a `channelData` property that specifies a single Telegram method. - -```json -"channelData": { - "method": "sendSticker", - "parameters": { - "sticker": { - "url": "https://domain.com/path/gif", - "mediaType": "image/gif", - } - } -} -``` - -This snippet shows an example of a `channelData` property that specifies an array of Telegram methods. - -```json -"channelData": [ - { - "method": "sendSticker", - "parameters": { - "sticker": { - "url": "https://domain.com/path/gif", - "mediaType": "image/gif", - } - } - }, - { - "method": "sendMessage", - "parameters": { - "text": "This message is HTML formatted.", - "parse_mode": "HTML" - } - } -] -``` - -## Create a native Kik message - -To create a native Kik message, set the activity object's channel data property to a JSON object that specifies this property: - -| Property | Description | -|----|----| -| messages | An array of Kik messages. For details about Kik message format, see Kik Message Formats. | - -This snippet shows an example of the `channelData` property for a native Kik message. - -```json -"channelData": { - "messages": [ - { - "chatId": "c6dd8165…", - "type": "link", - "to": "kikhandle", - "title": "My Webpage", - "text": "Some text to display", - "url": "http://botframework.com", - "picUrl": "http://lorempixel.com/400/200/", - "attribution": { - "name": "My App", - "iconUrl": "http://lorempixel.com/50/50/" - }, - "noForward": true, - "kikJsData": { - "key": "value" - } - } - ] -} -``` - -## Create a LINE message - -To create a message that implements LINE-specific message types (such as sticker, templates, or LINE specific action types like opening the phone camera), set the activity object's channel data property to a JSON object that specifies LINE message types and action types. - -| Property | Description | -|----|----| -| type | The LINE action/message type name | - -These LINE message types are supported: -* Sticker -* Imagemap -* Template (Button, confirm, carousel) -* Flex - -These LINE actions can be specified in the action field of the message type JSON object: -* Postback -* Message -* URI -* Datetimerpicker -* Camera -* Camera roll -* Location - -For details about these LINE methods and their parameters, see the [LINE Bot API documentation](https://developers.line.biz/en/docs/messaging-api/). - -This snippet shows an example of a `channelData` property that specifies a channel message type `ButtonTemplate` and 3 action types: camera, cameraRoll, Datetimepicker. - -```json -"channelData": { - "type": "ButtonsTemplate", - "altText": "This is a buttons template", - "template": { - "type": "buttons", - "thumbnailImageUrl": "https://example.com/bot/images/image.jpg", - "imageAspectRatio": "rectangle", - "imageSize": "cover", - "imageBackgroundColor": "#FFFFFF", - "title": "Menu", - "text": "Please select", - "defaultAction": { - "type": "uri", - "label": "View detail", - "uri": "http://example.com/page/123" - }, - "actions": [{ - "type": "cameraRoll", - "label": "Camera roll" - }, - { - "type": "camera", - "label": "Camera" - }, - { - "type": "datetimepicker", - "label": "Select date", - "data": "storeId=12345", - "mode": "datetime", - "initial": "2017-12-25t00:00", - "max": "2018-01-24t23:59", - "min": "2017-12-25t00:00" - } - ] - } -} -``` - -## Adding a bot to Teams - -Bots added to a team become another team member, who can be `@mentioned` as part of the conversation. In fact, bots only receive messages when they are `@mentioned`, so other conversations on the channel are not sent to the bot. -For more information, see [Channel and Group chat conversations with a Microsoft Teams bot](https://aka.ms/bots-con-channel). - -Because bots in a group or channel respond only when they are mentioned (`@botname`) in a message, every message received by a bot in a group channel contains its own name, and you must ensure your message parsing handles that. In addition, bots can parse out other users mentioned and mention users as part of their messages. - -### Check for and strip @bot mention - -```csharp - -Mention[] m = sourceMessage.GetMentions(); -var messageText = sourceMessage.Text; - -for (int i = 0;i < m.Length;i++) -{ - if (m[i].Mentioned.Id == sourceMessage.Recipient.Id) - { - //Bot is in the @mention list. - //The below example will strip the bot name out of the message, so you can parse it as if it wasn't included. Note that the Text object will contain the full bot name, if applicable. - if (m[i].Text != null) - messageText = messageText.Replace(m[i].Text, ""); - } -} -``` - -```javascript -var text = message.text; -if (message.entities) { - message.entities - .filter(entity => ((entity.type === "mention") && (entity.mentioned.id.toLowerCase() === botId))) - .forEach(entity => { - text = text.replace(entity.text, ""); - }); - text = text.trim(); -} - -``` - -> [!IMPORTANT] -> Adding a bot by GUID, for anything other than testing purposes, is not recommended. Doing so severely limits the functionality of a bot. Bots in production should be added to Teams as part of an app. See [Create a bot](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-create) and [Test and debug your Microsoft Teams bot](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-test). - - -## Additional resources - -- [Entities and activity types](../bot-service-activities-entities.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) diff --git a/articles/includes/snippet-code-csharp-intelligence-language.md b/articles/includes/snippet-code-csharp-intelligence-language.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/articles/includes/snippet-code-node-contactrelationupdate-1.md b/articles/includes/snippet-code-node-contactrelationupdate-1.md deleted file mode 100644 index 5592e3c2c..000000000 --- a/articles/includes/snippet-code-node-contactrelationupdate-1.md +++ /dev/null @@ -1,11 +0,0 @@ -```javascript -bot.on('contactRelationUpdate', function (message) { - if (message.action === 'add') { - var name = message.user ? message.user.name : null; - var reply = new builder.Message() - .address(message.address) - .text("Hello %s... Thanks for adding me.", name || 'there'); - bot.send(reply); - } -}); -``` \ No newline at end of file diff --git a/articles/includes/snippet-code-node-conversationupdate-1.md b/articles/includes/snippet-code-node-conversationupdate-1.md deleted file mode 100644 index fba92bd41..000000000 --- a/articles/includes/snippet-code-node-conversationupdate-1.md +++ /dev/null @@ -1,26 +0,0 @@ -```javascript -bot.on('conversationUpdate', function (message) { - if (message.membersAdded && message.membersAdded.length > 0) { - // Say hello - var isGroup = message.address.conversation.isGroup; - var txt = isGroup ? "Hello everyone!" : "Hello..."; - var reply = new builder.Message() - .address(message.address) - .text(txt); - bot.send(reply); - } else if (message.membersRemoved) { - // See if bot was removed - var botId = message.address.bot.id; - for (var i = 0; i < message.membersRemoved.length; i++) { - if (message.membersRemoved[i].id === botId) { - // Say goodbye - var reply = new builder.Message() - .address(message.address) - .text("Goodbye"); - bot.send(reply); - break; - } - } - } -}); -``` diff --git a/articles/includes/snippet-code-node-first-run-dialog-1.md b/articles/includes/snippet-code-node-first-run-dialog-1.md deleted file mode 100644 index bf46fe00a..000000000 --- a/articles/includes/snippet-code-node-first-run-dialog-1.md +++ /dev/null @@ -1,18 +0,0 @@ -```javascript -// Add first run dialog -bot.dialog('firstRun', function (session) { - session.userData.firstRun = true; - session.send("Hello...").endDialog(); -}).triggerAction({ - onFindAction: function (context, callback) { - // Only trigger if we've never seen user before - if (!context.userData.firstRun) { - // Return a score of 1.1 to ensure the first run dialog wins - callback(null, 1.1); - } else { - callback(null, 0.0); - } - } -}); - -``` \ No newline at end of file diff --git a/articles/includes/snippet-definition-turn.md b/articles/includes/snippet-definition-turn.md deleted file mode 100644 index 1f5ba9342..000000000 --- a/articles/includes/snippet-definition-turn.md +++ /dev/null @@ -1 +0,0 @@ -Receiving an activity, and subsequently processing it through your bot, is called a **turn**; this represents one complete cycle of your bot. A turn ends when all execution is done, the activity is fully processed and all the layers of the bot have completed. \ No newline at end of file diff --git a/articles/includes/snippet-deploy-considerations.md b/articles/includes/snippet-deploy-considerations.md deleted file mode 100644 index 714489d31..000000000 --- a/articles/includes/snippet-deploy-considerations.md +++ /dev/null @@ -1,22 +0,0 @@ -## Application settings and Messaging endpoint - -### Verify application settings - -For your bot to function properly in the cloud, you must ensure that its application settings are correct. -If you have an **appID** and **password**, -update the `Microsoft AppId` and `Microsoft App Password` values in your application's configuration settings as part of the deployment process. To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -> [!TIP] -> [!INCLUDE [Application configuration settings](~/includes/snippet-tip-bot-config-settings.md)] - -If you have not yet registered your bot with the Bot Framework (and therefore do not yet have an **appID** and **password**), -you can deploy your bot with temporary placeholder values for these settings. -Then later, after you [register your bot](~/bot-service-quickstart-registration.md), update your deployed application's settings with the **appID** and **password** values that were generated for your bot during registration. - -### Verify Messaging endpoint - -Your deployed bot must have an **Messaging endpoint** that can receive messages from the Bot Framework Connector Service. - -> [!NOTE] -> When you deploy your bot to Azure, SSL will automatically be configured for your application, thereby enabling the **Messaging endpoint** that the Bot Framework requires. -> If you deploy to another cloud service, be sure to verify that your application is configured for SSL so that the bot will have a **Messaging endpoint**. diff --git a/articles/includes/snippet-deploy-next-steps.md b/articles/includes/snippet-deploy-next-steps.md deleted file mode 100644 index 03961cd54..000000000 --- a/articles/includes/snippet-deploy-next-steps.md +++ /dev/null @@ -1,16 +0,0 @@ -## Next steps -After you have deployed your bot to the cloud and verified that the deployment was successful by testing the bot using the Bot Framework Emulator, the next step in the bot publication process will depend upon whether or not you've already registered your bot with the Bot Framework. - -### If you have already registered your bot with the Bot Framework: - -1. Return to the Bot Framework Portal and [update your bot's Settings data](~/bot-service-manage-settings.md) to specify the **Messaging endpoint** for the bot. - -2. [Configure the bot to run on one or more channels](~/bot-service-manage-channels.md). - -### If you have not yet registered your bot with the Bot Framework: - -1. [Register your bot with the Bot Framework](~/bot-service-quickstart-registration.md). - -2. Update the Microsoft App Id and Microsoft App Password values in your deployed application's configuration settings to specify the **appID** and **password** values that were generated for your bot during the registration process. To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -3. [Configure the bot to run on one or more channels](~/bot-service-manage-channels.md). \ No newline at end of file diff --git a/articles/includes/snippet-deploy-without-bot.md b/articles/includes/snippet-deploy-without-bot.md deleted file mode 100644 index 07ffca90b..000000000 --- a/articles/includes/snippet-deploy-without-bot.md +++ /dev/null @@ -1,124 +0,0 @@ -Before beginning the deployment, make sure you have the latest version of [Azure cli](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) and [dotnet cli](https://dotnet.microsoft.com/download). If you don't have dotnet cli, install it using the .Net Core Runtime option from the link provided above. - -### Login to Azure CLI and set your subscription -You've already created and tested a bot locally, and now you want to deploy it to Azure. Open a command prompt to log in to the Azure portal. - -```cmd -az login -``` -### Set the subscription - -Set the default subscription to use. - -```cmd -az account set --subscription "" -``` - -If you are not sure which subscription to use for deploying the bot, you can view the list of subscriptions for your account by using `az account list` command. - -Navigate to the bot folder. -`cd ` - -### Create a Web App Bot in Azure - -If you don't already have a resource group to which to publish your local bot, create one: - -```cmd -az group create --name --location --verbose -``` - -| Option | Description | -|:-----------|:---| -| name | A unique name for the resource group. DO NOT include spaces or underscores in the name. | -| location | Geographic location used to create the resource group. For example, `eastus`, `westus`, `westus2`, and so on. Use `az account list-locations` for a list of locations. | - -Then, create the bot resource into which you will publish your bot. This will provision the necessary resources in Azure and create a bot web app, which you will overwrite with your local bot. - -Before proceeding, read the instructions that apply to you based on the type of email account you use to log in to Azure. - -#### MSA email account -If you are using an MSA email account, you will need to create the app ID and app password on the Application Registration Portal to use with `az bot create` command. -1. Go to the [**Application Registration Portal**](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade). -1. Click on **Add an app** to register your application, create **Application Id**, and **Generate New Password**. If you already have an application and password but don't remember the password, you will have to generate a new password in the Application secrets section. -1. Save both application ID and the new password you just generated, so you that can use them with the `az bot create` command. - -```cmd -az bot create --kind webapp --name --location --version v4 --lang --verbose --resource-group --appid "" --password "" --verbose -``` - -| Option | Description | -|:---|:---| -| name | A unique name that is used to deploy the bot in Azure. It could be the same name as your local bot. DO NOT include spaces or underscores in the name. | -| location | Geographic location used to create the bot service resources. For example, `eastus`, `westus`, `westus2`, and so on. | -| resource-group | Name of resource group in which to create the bot. You can configure the default group using `az configure --defaults group=`. | -| appid | The Microsoft account ID (MSA ID) to be used with the bot. | -| password | The Microsoft account (MSA) password for the bot. | - -#### Business or school account - -```cmd -az bot create --kind webapp --name --location --version v4 --lang --verbose --resource-group -``` -| Option | Description | -|:---|:---| -| name | A unique name that is used to deploy the bot in Azure. It could be the same name as your local bot. DO NOT include spaces or underscores in the name. | -| location | Geographic location used to create the bot service resources. For example, `eastus`, `westus`, `westus2`, and so on. | -| lang | The language to use to create the bot: `Csharp`, or `Node`; default is `Csharp`. | -| resource-group | Name of resource group in which to create the bot. You can configure the default group using `az configure --defaults group=`. | - -#### Update appsettings.json or .env file -After the bot is created, you should see the following information displayed in the console window: - -```JSON -{ - "appId": "as234-345b-4def-9047-a8a44b4s", - "appPassword": "34$#w%^$%23@334343", - "endpoint": "https://mybot.azurewebsites.net/api/messages", - "id": "mybot", - "name": "mybot", - "resourceGroup": "botresourcegroup", - "serviceName": "mybot", - "subscriptionId": "234532-8720-5632-a3e2-a1qw234", - "tenantId": "32f955bf-33f1-43af-3ab-23d009defs47", - "type": "abs" -} -``` - -You'll need to copy the `appId` and `appPassword` values and paste them into the appsettings.json or .env file. For example: - -```JSON -{ - MicrosoftAppId: "as234-345b-4def-9047-a8a44b4s", - MicrosoftAppPassword: "34$#w%^$%23@334343" -} -``` -Note that if your appsettings.json or .env file has additional keys for other services you've provisioned for your bot, don't delete those entries. - -Save the file. - -Next, depending on the programming langauge (**C#** or **JS**) you used to create the bot, follow the steps that apply to you. - -**C# Bot:** - -Open a command-prompt, and navigate to the project folder. Run the following commands from the command line. - -| Task | Command | -|:-----|:--------| -| 1. Restore project dependencies | `dotnet restore`| -| 2. Build the project | `dotnet build` | -| 3. Zip project files | Use any utility to zip the project files. Go to the folder that has the .csproj file and select all the files and folder at this level to create the zipped folder. | -| 4. Set build deployment setting | `az webapp config appsettings set --resource-group --name --settings SCM_DO_BUILD_DEPLOYMENT=false`| -| 5. Set the script generator args | `az webapp config appsettings set --resource-group --name --settings SCM_SCRIPT_GENERATOR_ARGS="--aspNetCore mybot.csproj"`| - -**JS Bot:** -1. Download web.config from [here](https://github.com/projectkudu/kudu/wiki/Using-a-custom-web.config-for-Node-apps) and save it into your project folder. -1. Edit the file and replace all occurances of "server.js" with "index.js". -1. Save the file. - -Open a command-prompt, and navigate to the project folder. Run the following commands from the command line. - -| Task | Command | -|:-----|:--------| -| 1. Install node modules | `npm install` | -| 2. Zip project files | Use any utility to zip the project files. Go to the folder that has the .csproj file and select all the files and folder at this level to create the zipped folder. | -| 3. Set build deployment setting | `az webapp config appsettings set --resource-group --name --settings SCM_DO_BUILD_DEPLOYMENT=false`| diff --git a/articles/includes/snippet-dotnet-concept-activity.md b/articles/includes/snippet-dotnet-concept-activity.md deleted file mode 100644 index dfbb58d1f..000000000 --- a/articles/includes/snippet-dotnet-concept-activity.md +++ /dev/null @@ -1,2 +0,0 @@ -The [Connector](~/dotnet/bot-builder-dotnet-concepts.md#connector) uses an Activity object to pass information back and forth between bot and channel (user). -The most common type of activity is **message**, but there are other activity types that can be used to communicate various types of information to a bot or channel. \ No newline at end of file diff --git a/articles/includes/snippet-dotnet-concept-state.md b/articles/includes/snippet-dotnet-concept-state.md deleted file mode 100644 index 4c0d4e4ea..000000000 --- a/articles/includes/snippet-dotnet-concept-state.md +++ /dev/null @@ -1,7 +0,0 @@ -The Bot Builder Framework enables your bot to store and retrieve state data that is associated with a user, a conversation, or a specific user within the context of a specific conversation. -State data can be used for many purposes, such as determining where the prior conversation left off or simply greeting a returning user by name. If you store a user's preferences, you can use that information to customize the conversation the next time you chat. For example, you might alert the user to a news article about a topic that interests them, or alert a user when an appointment becomes available. - -For testing and prototyping purposes, you can use the Bot Builder Framework's in-memory data storage. For production bots, you can implement your own storage adapter or use one of Azure Extensions. The Azure Extensions allow you to store your bot's state data in either Table Storage, CosmosDB, or SQL. This article will show you how to use the in-memory storage adapter to store your bot's state data. - -> [!IMPORTANT] -> The Bot Framework State Service API is not recommended for production environments, and may be deprecated in a future release. It is recommended that you update your bot code to use the in-memory storage adapter for testing purposes or use one of the **Azure Extensions** for production bots. diff --git a/articles/includes/snippet-dotnet-formflow-samples.md b/articles/includes/snippet-dotnet-formflow-samples.md deleted file mode 100644 index 81063db6a..000000000 --- a/articles/includes/snippet-dotnet-formflow-samples.md +++ /dev/null @@ -1,2 +0,0 @@ -For complete samples that show how to implement FormFlow using the Bot Framework SDK for .NET, see the Multi-Dialog Bot sample and the Contoso Flowers Bot sample in GitHub. - diff --git a/articles/includes/snippet-dotnet-manage-conversation-flow-intro.md b/articles/includes/snippet-dotnet-manage-conversation-flow-intro.md deleted file mode 100644 index d5d96ee33..000000000 --- a/articles/includes/snippet-dotnet-manage-conversation-flow-intro.md +++ /dev/null @@ -1,13 +0,0 @@ -This diagram shows the screen flow of a traditional application compared to the dialog flow of a bot. - -![bot](~/media/designing-bots/core/dialogs-screens.png) - -In a traditional application, everything begins with the **main screen**. -The **main screen** invokes the **new order screen**. -The **new order screen** remains in control until it either closes or invokes other screens. -If the **new order screen** closes, the user is returned to the **main screen**. - -In a bot, everything begins with the **root dialog**. -The **root dialog** invokes the **new order dialog**. -At that point, the **new order dialog** takes control of the conversation and remains in control until it either closes or invokes other dialogs. -If the **new order dialog** closes, control of the conversation is returned back to the **root dialog**. \ No newline at end of file diff --git a/articles/includes/snippet-entity-boilerplate.md b/articles/includes/snippet-entity-boilerplate.md deleted file mode 100644 index 94e533d4f..000000000 --- a/articles/includes/snippet-entity-boilerplate.md +++ /dev/null @@ -1,13 +0,0 @@ -::: moniker range="azure-bot-service-3.0" -> [!NOTE] -> Different parts of the SDK define separate Entity classes or elements. -> - For message entities, see [Entity and activity types](https://docs.microsoft.com/azure/bot-service/bot-service-activities-entities?view=azure-bot-service-4.0). -> - For LUIS recognition entities, see [Recognize intents and entities with LUIS](../nodejs/bot-builder-nodejs-recognize-intent-luis.md). -::: moniker-end - -::: moniker range="azure-bot-service-4.0" -> [!NOTE] -> Different parts of the SDK define separate Entity classes or elements. -> - For message entities, see [Entity and activity types](https://docs.microsoft.com/azure/bot-service/bot-service-activities-entities?view=azure-bot-service-4.0). -> - For LUIS recognition entities, see [Extract entities](../v4sdk/bot-builder-howto-v4-luis.md). -::: moniker-end diff --git a/articles/includes/snippet-getstarted-test-bot.md b/articles/includes/snippet-getstarted-test-bot.md deleted file mode 100644 index 55bf2e9a3..000000000 --- a/articles/includes/snippet-getstarted-test-bot.md +++ /dev/null @@ -1,6 +0,0 @@ -Next, test your bot by using the [Bot Framework Emulator](~/bot-service-debug-emulator.md) to see it in action. -The emulator is a desktop application that lets you test and debug your bot on localhost or running remotely through a tunnel. - -First, you'll need to download and install the emulator. -Click [here](https://emulator.botframework.com/) to download the emulator. After the download completes, launch the executable and complete the installation process. - diff --git a/articles/includes/snippet-global-handlers-intro.md b/articles/includes/snippet-global-handlers-intro.md deleted file mode 100644 index 4d41eae68..000000000 --- a/articles/includes/snippet-global-handlers-intro.md +++ /dev/null @@ -1,10 +0,0 @@ -Users commonly attempt to access certain functionality within a bot by using keywords like "help", "cancel", or "start over". -This often occurs in the middle of a conversation, when the bot is expecting a different response. -By implementing **global message handlers**, you can design your bot to gracefully handle such requests. -The handlers will examine user input for the keywords that you specify, such as "help", "cancel", or "start over", and respond appropriately. - -![how users talk](~/media/designing-bots/capabilities/trigger-actions.png) - -> [!NOTE] -> By defining the logic in global message handlers, you're making it accessible to all dialogs. -> Individual dialogs and prompts can be configured to safely ignore the keywords. diff --git a/articles/includes/snippet-message-logging-intro.md b/articles/includes/snippet-message-logging-intro.md deleted file mode 100644 index e173d6a5f..000000000 --- a/articles/includes/snippet-message-logging-intro.md +++ /dev/null @@ -1,11 +0,0 @@ -The **middleware** functionality in the Bot Framework SDK enables your bot to intercept all messages that are exchanged between user and bot. -For each message that is intercepted, you may choose to do things such as -save the message to a data store that you specify, which creates a conversation log, or -inspect the message in some way and take whatever action your code specifies. - -> [!NOTE] -> The Bot Framework does not automatically save conversation details, -> as doing so could potentially capture private information that bots and users do not wish to share -> with outside parties. -> If your bot saves conversation details, -> it should communicate that to the user and describe what will be done with the data. \ No newline at end of file diff --git a/articles/includes/snippet-payment-process-callbacks-1.md b/articles/includes/snippet-payment-process-callbacks-1.md deleted file mode 100644 index c5f21dabe..000000000 --- a/articles/includes/snippet-payment-process-callbacks-1.md +++ /dev/null @@ -1,10 +0,0 @@ -When receiving a Shipping Address Update or a Shipping Option Update callback, -your bot will be provided with the current state of the payment details from the client in the `Activity.Value` property. -As a merchant, you should treat these callbacks as static, given input payment details you will calculate some output payment details and -fail if the input state provided by the client is invalid for any reason.  -If the bot determines the given information is valid as-is, simply send HTTP status code `200 OK` along with the unmodified payment -details. Alternatively, the bot may send HTTP status code `200 OK` along with an updated payment details that should be applied before the order can be processed. -In some cases, your bot may determine that the updated information is invalid and the -order cannot be processed as-is. For example, the user's shipping address may specify a country to which the -product supplier does not ship. In that case, the bot may send HTTP status code `200 OK` and a message populating the error property of the payment details object. -Sending any HTTP status code in the `400` or `500` range to will result in a generic error for the customer. diff --git a/articles/includes/snippet-payment-process-callbacks-overview.md b/articles/includes/snippet-payment-process-callbacks-overview.md deleted file mode 100644 index ba93ae44b..000000000 --- a/articles/includes/snippet-payment-process-callbacks-overview.md +++ /dev/null @@ -1 +0,0 @@ -When your bot receives a callback, it should verify that the information specified in the callback is valid and acknowledge the callback by sending an HTTP response. \ No newline at end of file diff --git a/articles/includes/snippet-payment-process-overview.md b/articles/includes/snippet-payment-process-overview.md deleted file mode 100644 index 478c9be24..000000000 --- a/articles/includes/snippet-payment-process-overview.md +++ /dev/null @@ -1,11 +0,0 @@ -## Payment process overview - -The payment process comprises three distinct parts: - -1. The bot sends a payment request. - -2. The user signs in with a Microsoft account to provide payment, shipping, and contact information. Callbacks are sent to the bot to indicate when the bot needs to perform certain operations (update shipping address, update shipping option, complete payment). - -3. The bot processes the callbacks that it receives, including shipping address update, shipping option update, and payment complete. - -Your bot must implement only step one and step three of this process; step two takes place outside the context of your bot. diff --git a/articles/includes/snippet-payment-test-bot.md b/articles/includes/snippet-payment-test-bot.md deleted file mode 100644 index b2acc691f..000000000 --- a/articles/includes/snippet-payment-test-bot.md +++ /dev/null @@ -1,7 +0,0 @@ -To fully test a bot that requests payment, [configure](~/bot-service-manage-channels.md) -it to run on channels that support Bot Framework payments, like Web Chat and Skype. -Alternatively, you can test your bot locally using the [Bot Framework Emulator](~/bot-service-debug-emulator.md). - -> [!TIP] -> Callbacks are sent to your bot when a user changes data or clicks **Pay** during the payment web experience. -> Therefore, you can test your bot's ability to receive and process callbacks by interacting with the payment web experience yourself. diff --git a/articles/includes/snippet-proactive-messages-intro-1.md b/articles/includes/snippet-proactive-messages-intro-1.md deleted file mode 100644 index 2e4dac275..000000000 --- a/articles/includes/snippet-proactive-messages-intro-1.md +++ /dev/null @@ -1,28 +0,0 @@ -Typically, each message that a bot sends to the user directly relates to the user's prior input. -In some cases, a bot may need to send the user a message that is not directly related to the current topic of conversation or to the last message the user sent. These types of messages are called **proactive messages**. - -Proactive messages can be useful in a variety of scenarios. -If a bot sets a timer or reminder, it will need to notify the user when the time arrives. -Or, if a bot receives a notification from an external system, it may need to communicate that information to the user immediately. -For example, if the user has previously asked the bot to monitor the price of a product, the bot can alert the user if the price of the product has dropped by 20%. Or, if a bot requires some time to compile a response to the user's question, it may inform the user of the delay and allow the conversation to continue in the meantime. When the bot finishes compiling the response to the question, it will share that information with the user. - -When implementing proactive messages in your bot: - -- *Don't* send several proactive messages within a short amount of time. Some channels enforce restrictions on how frequently a bot can send messages to the user, and will disable the bot if it violates those restrictions. -- *Don't* send proactive messages to users who have not previously interacted with the bot or solicited contact with the bot through another means such as e-mail or SMS. - -Proactive messages can create unexpected behavior. Consider the following scenario. - -![how users talk](~/media/designing-bots/capabilities/proactive1.png) - -In this example, the user has previously asked the bot to monitor prices of a hotel in Las Vegas. -The bot launched a background monitoring task, which has been running continuously for the past several days. -In the conversation, the user is currently booking a trip to London when the background task triggers a notification message about a discount for the Las Vegas hotel. The bot interjects this information into the conversation, making for a confusing user experience. - -How should the bot have handled this situation? - -- Wait for the current travel booking to finish, then deliver the notification. This approach would be minimally disruptive, but the delay in communicating the information might cause the user to miss out on the low-price opportunity for the Las Vegas hotel. -- Cancel the current travel booking flow and deliver the notification immediately. This approach delivers the information in a timely fashion but would likely frustrate the user by forcing them start over with their travel booking. -- Interrupt the current booking, clearly change the topic of conversation to the hotel in Las Vegas until the user responds, and then switch back to the in-progress travel booking and continue from where it was interrupted. This approach may seem like the best choice, but it introduces complexity both for the bot developer and the user. - -Most commonly, your bot will use some combination of **ad hoc proactive messages** and other techniques to handle situations like this. diff --git a/articles/includes/snippet-proactive-messages-intro-2.md b/articles/includes/snippet-proactive-messages-intro-2.md deleted file mode 100644 index c0ee7794a..000000000 --- a/articles/includes/snippet-proactive-messages-intro-2.md +++ /dev/null @@ -1,22 +0,0 @@ -An **ad hoc proactive message** is the simplest type of proactive message. -The bot simply interjects the message into the conversation whenever it is triggered, without any regard for whether the user is currently engaged in a separate topic of conversation with the bot and will not attempt to change the conversation in any way. - -To handle notifications more smoothly, consider other ways to integrate the notification into the conversation flow, such as setting a flag in the conversation state or adding the notification to a queue. - - \ No newline at end of file diff --git a/articles/includes/snippet-publish-to-channel.md b/articles/includes/snippet-publish-to-channel.md deleted file mode 100644 index 039c0268a..000000000 --- a/articles/includes/snippet-publish-to-channel.md +++ /dev/null @@ -1,12 +0,0 @@ -### Cortana -Bots are published to Cortana from the [dashboard](https://aka.ms/cortana-publish) and are used to power Cortana skills. Publishing a bot submits it for review. Cortana skills can be deployed for your own use, deployed to a small group, or published to the world. - -### Skype -Bots are published to Skype from the [configuration page](~/bot-service-channel-connect-skype.md). Publishing a bot submits it for review. Before review, the bot is limited to 100 contacts. Approved bots do not have limited contacts and you may opt to have the bot included in the Skype bot directory. - -### Skype for Business -Skype for Business bots are registered with a [Skype for Business Online tenant](https://msdn.microsoft.com/skype/Skype-For-Business-Bot-Framework/docs/overview) by a Tenant Administrator. - -> [!TIP] -> To view the status of a review, open the bot in the [Bot Framework Portal](https://dev.botframework.com/) and click **Channels**. -> If the bot is not approved, the result will link to the reason why. After making the required changes, resubmit the bot for review. diff --git a/articles/includes/snippet-quickstart-paths.md b/articles/includes/snippet-quickstart-paths.md deleted file mode 100644 index 8dc343eb8..000000000 --- a/articles/includes/snippet-quickstart-paths.md +++ /dev/null @@ -1 +0,0 @@ -Creating a bot with Azure Bot Service and creating a bot locally are independent, parallel ways to create a bot. \ No newline at end of file diff --git a/articles/includes/snippet-suggested-actions-intro.md b/articles/includes/snippet-suggested-actions-intro.md deleted file mode 100644 index 36a043537..000000000 --- a/articles/includes/snippet-suggested-actions-intro.md +++ /dev/null @@ -1,3 +0,0 @@ -Suggested actions enable your bot to present buttons that the user can tap to provide input. -Suggested actions appear close to the composer and enhance user experience by enabling the user to answer a question or make a selection with a simple tap of a button, rather than having to type a response with a keyboard. -Unlike buttons that appear within rich cards (which remain visible and accessible to the user even after being tapped), buttons that appear within the suggested actions pane will disappear after the user makes a selection. This prevents the user from tapping stale buttons within a conversation and simplifies bot development (since you will not need to account for that scenario). \ No newline at end of file diff --git a/articles/includes/snippet-tip-bot-config-settings.md b/articles/includes/snippet-tip-bot-config-settings.md deleted file mode 100644 index 220d7884f..000000000 --- a/articles/includes/snippet-tip-bot-config-settings.md +++ /dev/null @@ -1,7 +0,0 @@ -> If you're using the Bot Framework SDK for Node.js, set the following environment variables: ->
  • MICROSOFT_APP_ID
  • MICROSOFT_APP_PASSWORD
-> If you're using the Bot Framework SDK for .NET, set the following key values in the web.config file: ->
  • MicrosoftAppId
  • MicrosoftAppPassword
- -> [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). \ No newline at end of file diff --git a/articles/includes/snippet-verify-deployment-using-emulator.md b/articles/includes/snippet-verify-deployment-using-emulator.md deleted file mode 100644 index c520f782f..000000000 --- a/articles/includes/snippet-verify-deployment-using-emulator.md +++ /dev/null @@ -1,5 +0,0 @@ -Verify the deployment of your bot by using the [Bot Framework Emulator](~/bot-service-debug-emulator.md). - -Enter the bot's **Messaging endpoint** into the address bar of the Emulator. If you built your bot with the Bot Framework SDK, the endpoint should end with */api/messages*. - -Example: `https://.azurewebsites.net/api/messages` diff --git a/articles/includes/vsix-templates-versions.md b/articles/includes/vsix-templates-versions.md new file mode 100644 index 000000000..78560e07c --- /dev/null +++ b/articles/includes/vsix-templates-versions.md @@ -0,0 +1,18 @@ +--- +ms.topic: include +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +> [!NOTE] +> You can install the templates from within Visual Studio. +> +> 1. In the menu, select **Extensions** then **Manage Extensions**. +> 1. In the **Manage Extensions** dialog, search for and install **Bot Framework v4 SDK templates for Visual Studio**. +> +> For information about deploying .NET bots to Azure, see how to [Provision and publish a bot](../provision-and-publish-a-bot.md). diff --git a/articles/index-bf-sdk.yml b/articles/index-bf-sdk.yml new file mode 100644 index 000000000..53b05ef2c --- /dev/null +++ b/articles/index-bf-sdk.yml @@ -0,0 +1,155 @@ +### YamlMime:Landing + +title: Bot Framework SDK documentation +summary: The Bot Framework SDK allows you to create and develop bots for Azure AI Bot Service. + +metadata: + title: Bot Framework SDK documentation + description: The Bot Framework SDK allows you to create and develop bots for Azure AI Bot Service. + ms.service: azure-ai-bot-service + ms.topic: landing-page + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + +landingContent: +# Cards and links should be based on top customer tasks or top subjects +# Start card title with a verb + # Card (optional) + - title: About the SDK + linkLists: + - linkListType: overview + links: + - text: Choose the right chatbot solution + url: bot-overview.md + - text: What is the Bot Framework SDK? + url: bot-service-overview.md + - text: Regionalization support + url: v4sdk/bot-builder-concept-regionalization.md + - linkListType: concept + links: + - text: How bots work + url: v4sdk/bot-builder-basics.md + - text: How skill bots work + url: v4sdk/skills-conceptual.md + + # Card + - title: Get started + linkLists: + - linkListType: quickstart + links: + - text: Create a basic bot + url: bot-service-quickstart-create-bot.md + - text: Create an Azure Bot resource + url: v4sdk/abs-quickstart.md + - linkListType: how-to-guide + links: + - text: Send and receive messages + url: v4sdk/bot-builder-howto-send-messages.md + - text: Principles of bot design + url: bot-service-design-principles.md + + # Card + - title: Manage conversation flow + linkLists: + - linkListType: concept + links: + - text: About the dialogs library + url: v4sdk/bot-builder-concept-dialog.md + - linkListType: how-to-guide + links: + - text: Sequential conversation flow + url: v4sdk/bot-builder-dialog-manage-conversation-flow.md + - text: Manage dialog complexity + url: v4sdk/bot-builder-compositcontrol.md + - text: Advanced conversation flow + url: v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md + + # Card + - title: Test a bot + linkLists: + - linkListType: how-to-guide + links: + - text: Test and debug with the Emulator + url: bot-service-debug-emulator.md + - text: Add trace activities to your bot + url: v4sdk/using-trace-activities.md + - text: Test a published bot with the Emulator + url: bot-service-debug-channel-devtunnel.md + - text: Debug a skill or skill consumer + url: v4sdk/skills-debug-skill-or-consumer.md + + # Card + - title: Publish a bot + linkLists: + - linkListType: how-to-guide + links: + - text: Deploy your bot to Azure + url: provision-and-publish-a-bot.md + - text: Set up continuous deployment + url: bot-service-build-continuous-deployment.md + - text: Configure bot settings + url: bot-service-manage-settings.md + - text: Connect a bot to channels + url: bot-service-manage-channels.md + + # Card + - title: Add authentication + linkLists: + - linkListType: concept + links: + - text: Bot authentication + url: v4sdk/bot-builder-concept-authentication.md + - linkListType: how-to-guide + links: + - text: Add authentication to a bot + url: v4sdk/bot-builder-authentication.md + - linkListType: reference + links: + - text: Troubleshoot authentication + url: bot-service-troubleshoot-authentication-problems.md + + # Card + - title: Languages + linkLists: + - linkListType: reference + links: + - text: .NET + url: /dotnet/api/?view=botbuilder-dotnet-stable + - text: JavaScript + url: /javascript/api/botbuilder + - text: Java (retiring Nov 2023) + url: /java/api/?view=botbuilder-java + - text: Python(retiring Nov 2023) + url: /python/api/?view=botbuilder-py-latest + + # Card + - title: Need Help? + linkLists: + - linkListType: reference + links: + - text: Get support + url: bot-service-resources-links-help.md + - linkListType: sample + links: + - text: Samples + url: https://github.com/Microsoft/BotBuilder-Samples/#readme + - text: Bot Builder Community repos + url: https://github.com/BotBuilderCommunity + + # Card + - title: Resources + linkLists: + - linkListType: overview + links: + - text: Copilot Studio + url: /microsoft-copilot-studio/ + - text: Microsoft Teams overview + url: /microsoftteams/teams-overview + - text: Azure AI services + url: /azure/ai-services + - linkListType: concept + links: + - text: How Microsoft Teams bots work + url: v4sdk/bot-builder-basics-teams.md diff --git a/articles/index.yml b/articles/index.yml index a016ffe82..6dd8bc6bb 100644 --- a/articles/index.yml +++ b/articles/index.yml @@ -1,88 +1,162 @@ -### YamlMime:YamlDocument -documentType: LandingData -title: Azure Bot Service Documentation +### YamlMime:Hub + +title: Azure AI Bot Service documentation +summary: Azure AI Bot Service provides an integrated environment that is purpose-built for bot development. +brand: azure + metadata: - document_id: 1d934240-28c7-635f-d41b-3e43ae02970c - title: Azure Bot Service Documentation - Tutorials, API Reference - meta.description: Learn how to create bots. Documentation helps you create, deploy, and manage bots in the cloud. - author: ivorb - manager: kamrani - ms.service: bot-service - ms.tgt_pltfrm: na - ms.devlang: na - ms.topic: landing-page - ms.date: 11/11/2019 - ms.author: ivorb -abstract: - description: Azure Bot Service provides an integrated environment that is purpose-built for bot development, enabling you to build, connect, test, deploy, and manage intelligent bots, all from one place. Azure Bot Service leverages the Bot Framework SDK with support for C# and JavaScript. Learn how to use Bot Service with our quickstarts, tutorials, and samples. - aside: - image: - alt: - height: 110 - src: media/video/satya-video.png - width: 250 - title: Ignite 2019 keynote by Satya Nadella. (1:52) - href: https://myignite.techcommunity.microsoft.com/sessions/77831?source=sessions - width: 250 -sections: -- items: - - type: list - style: cards - className: cardsM - columns: 3 - items: - - href: https://docs.microsoft.com/azure/bot-service/what-is-new - html:

What's new in Azure Bot Service.

- image: - src: media/index/i_guidelines.png - title: What's new? - - href: https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction - html:

Learn about Azure Bot Service.

- image: - src: media/index/i_overview.png - title: What is Azure Bot Service? - - href: https://docs.microsoft.com/azure/bot-service/bot-builder-basics - html:

Understand how bots work.

- image: - src: media/index/i_guidelines.png - title: How bots work -- title: 5-Minute Quickstarts - items: - - type: paragraph - text: 'Learn how to create your first bot:' - - type: list - style: icon48 - items: - - image: - src: v4sdk/media/logo_csharp.svg - text: C# - href: https://docs.microsoft.com/azure/bot-service/dotnet/bot-builder-dotnet-sdk-quickstart - - image: - src: v4sdk/media/logo_js.svg - text: JavaScript - href: https://docs.microsoft.com/azure/bot-service/javascript/bot-builder-javascript-quickstart - - image: - src: v4sdk/media/logo_python.svg - text: Python - href: https://docs.microsoft.com/azure/bot-service/python/bot-builder-python-quickstart - - image: - src: media/index/azure_portal.png - text: Azure Portal - href: https://docs.microsoft.com/azure/bot-service/bot-service-quickstart -- title: Step-by-Step Tutorials + title: Azure AI Bot Service documentation + description: Azure AI Bot Service provides an integrated environment that is purpose-built for bot development. + services: service + ms.service: azure-ai-bot-service + ms.topic: hub-page + author: JonathanFingold + ms.author: iawilt + manager: shellyha + ms.reviewer: micchow + +# highlightedContent section (optional) +# Maximum of 8 items +highlightedContent: +# itemType: architecture | concept | deploy | download | get-started | how-to-guide | learn | overview | quickstart | reference | sample | tutorial | video | whats-new + items: + # Card + # Comparison of Copilot Studio, Bot Framework Composer, and Bot SDK + - title: "Choose the right chatbot solution" + itemType: overview + url: bot-overview.md + # Card + # Primary CTA + - title: "What is Microsoft Copilot Studio" + itemType: overview + url: /microsoft-copilot-studio/fundamentals-what-is-copilot-studio + # Card + # Primary CTA + - title: "Create a Bot Framework SDK bot" + itemType: get-started + url: bot-service-quickstart-create-bot.md + +# productDirectory section (optional) +productDirectory: + title: Development options + summary: There's more than one way to build and deploy a chatbot. Let's take a look at some options. items: - - type: paragraph - text: Learn how to create and deploy bots on Azure. - - type: list - style: ordered - items: - - html: Create and deploy a simple bot - - html: Add QnA Maker and redeploy a bot -- title: Reference + # Card + - title: Bot Framework SDK + imageSrc: ./media/logos/logo-bot-sdk.svg + summary: A modular set of developer tools to build, test, deploy, and manage chatbots in C# or JavaScript. + url: bot-service-overview-introduction.md + # Card + - title: Copilot Studio + imageSrc: ./media/logos/logo-copilot-studio.svg + summary: Create agents with an easy to use graphical interface. No coding or AI expertise required. Works with Power Apps, and extendable with code. + url: /microsoft-copilot-studio/fundamentals-what-is-copilot-studio + # Card + - title: Bot Framework Composer + imageSrc: ./media/logos/logo-composer.svg + summary: An open-source IDE built on the Bot Framework SDK. Composer lets you build chatbots with a visual authoring canvas, and extend with code. + url: /composer/introduction + +conceptualContent: items: - - type: list - style: cards - className: cardsD - items: - - title: Languages - html:

.NET

JavaScript

+ # Card + - title: Bot Framework SDK + links: + - url: bot-service-overview-introduction.md + itemType: overview + text: What is Bot Framework SDK? + - url: bot-service-quickstart-create-bot.md + itemType: quickstart + text: Create a bot with the SDK + - url: tutorial-provision-a-bot.md + itemType: tutorial + text: Provision and publish a bot to Azure + - url: bot-service-manage-channels.md + itemType: how-to-guide + text: Connect a bot to channels with the SDK + # Card + - title: Copilot Studio + links: + - url: /microsoft-copilot-studio/fundamentals-what-is-copilot-studio + itemType: overview + text: What is Copilot Studio? + - url: /microsoft-copilot-studio/fundamentals-get-started + itemType: quickstart + text: Quickstart - Create your first agent + - url: /microsoft-copilot-studio/requirements-quotas + itemType: reference + text: Quotas, limits, and configuration options + # Card + - title: Bot Framework Composer + links: + - url: /composer/introduction + itemType: overview + text: What is Bot Framework Composer? + - url: /composer/install-composer + itemType: get-started + text: Install Composer for Windows, macOS, or Linux + - url: /composer/quickstart-create-bot + itemType: quickstart + text: Create your first bot with Composer + - url: /composer/tutorial/tutorial-introduction + itemType: tutorial + text: Build a weather bot with Composer +tools: + title: Reference # < 60 chars (optional) + summary: Dive deep into reference for the Bot Framework SDK, Copilot Studio, and Bot Framework Composer. # < 160 chars (optional) + items: + - title: C# + imageSrc: ./media/logos/logo-csharp.svg + url: /dotnet/api/?view=botbuilder-dotnet-stable + - title: JavaScript + imageSrc: ./media/logos/logo-js.svg + url: /javascript/api/?view=botbuilder-ts-latest + - title: Copilot Studio + imageSrc: ./media/logos/logo-copilot-studio.svg + url: /microsoft-copilot-studio/fundamentals-what-is-copilot-studio + - title: Composer + imageSrc: ./media/logos/logo-composer.svg + url: /composer/install-composer + +additionalContent: + # Supports up to 3 sections + sections: + - title: Additional resources # < 60 chars (optional) + items: + # Card + - title: Tools + links: + - text: Bot Framework CLI + url: https://github.com/microsoft/botframework-cli#readme + - text: Bot Framework Emulator + url: https://github.com/Microsoft/BotFramework-Emulator#readme + - text: Web Chat + url: https://github.com/Microsoft/BotFramework-WebChat#readme + - text: Bot Framework Orchestrator + url: https://github.com/microsoft/botframework-sdk/tree/main/Orchestrator#readme + # Card + - title: Learn modules # < 60 chars (optional) + links: + - text: Choose a bot building tool + url: /training/modules/choose-bot-building-tool/ + - text: Create a bot with the Bot Framework SDK + url: /training/modules/design-bot-conversation-flow/ + - text: Build an initial agent with Microsoft Copilot Studio + url: /training/modules/create-copilots-copilot-studio/ + - text: Introduction to Azure AI Bot Service and Bot Framework Composer + url: /training/modules/intro-to-bot-service-bot-framework-composer/ + # Card + - title: Related technologies # < 60 chars (optional) + links: + - text: Copilot Studio + url: /microsoft-copilot-studio + - text: Microsoft Teams + url: /microsoftteams/platform + - text: Adaptive Cards + url: /adaptive-cards + - text: Azure AI services + url: /azure/ai-services + - text: Azure AI Language + url: /azure/ai-services/language-service + - text: Azure AI Speech + url: /azure/ai-services/speech-service diff --git a/articles/java/bot-builder-java-samples.md b/articles/java/bot-builder-java-samples.md deleted file mode 100644 index e95b2e159..000000000 --- a/articles/java/bot-builder-java-samples.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Sample bots for Bot Framework SDK for Java - Bot Service -description: Explore sample bots that can help kickstart your bot development with the Bot Framework SDK for Java. -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Bot Framework SDK v4 Java samples -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] - -You can use the samples to help you build bots using Java SDK v4, which is currently in preview. - -## Get the samples -To get the samples, clone the [botbuilder-java](https://github.com/Microsoft/botbuilder-java) GitHub repository using Git. - -```cmd -git clone https://github.com/Microsoft/botbuilder-java.git -cd samples -``` -The sample bots built with the Bot Framework SDK for Java are organized in the **samples** directory. diff --git a/articles/javascript/bot-builder-javascript-quickstart.md b/articles/javascript/bot-builder-javascript-quickstart.md deleted file mode 100644 index eb0d3a857..000000000 --- a/articles/javascript/bot-builder-javascript-quickstart.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Create a bot using Bot Framework SDK for JavaScript - Bot Service -description: Quickly create a bot using the Bot Framework SDK for JavaScript. -keywords: quickstart, bot framework sdk, getting started -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Create a bot with the Bot Framework SDK for JavaScript - -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] - -This quickstart walks you through building a single bot by using the Yeoman Bot Builder generator and the Bot Framework SDK for JavaScript, and then testing it with the Bot Framework Emulator. - -[!INCLUDE [Azure vs local development](~/includes/snippet-quickstart-paths.md)] - -[!INCLUDE [javascript quickstart](~/includes/quickstart-javascript.md)] - -## Additional resources - -See [tunneling (ngrok)](https://github.com/Microsoft/BotFramework-Emulator/wiki/Tunneling-(ngrok)) for how to connect to a bot hosted remotely. - -## Next steps - -> [!div class="nextstepaction"] -> [Deploy your bot to Azure](../bot-builder-deploy-az-cli.md) diff --git a/articles/javascript/bot-builder-javascript-samples.md b/articles/javascript/bot-builder-javascript-samples.md deleted file mode 100644 index 97ab30938..000000000 --- a/articles/javascript/bot-builder-javascript-samples.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Sample bots for Bot Framework SDK for JavaScript - Bot Service -description: Explore a large selection of sample bots that can help kickstart your bot development with the Bot Framework SDK for JavaScript. -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# JavaScript samples for Bot Framework SDK -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] - -Samples in the Bot Builder Samples repo demonstrate task-focused bots that show how to take advantage of features provided in the SDK for JavaScript. You can use the samples to help you quickly get started with building great bots with rich capabilities. Refer to [readme](https://github.com/Microsoft/BotBuilder-Samples/blob/master/README.md) file for sample list and additional informaiton. - -To get the samples, clone the [botbuilder-samples](https://github.com/Microsoft/botbuilder-samples) GitHub repository using Git. -```cmd -git clone https://github.com/Microsoft/botbuilder-dotnet.git -``` diff --git a/articles/language-generation/functions-injected-from-language-generation.md b/articles/language-generation/functions-injected-from-language-generation.md new file mode 100644 index 000000000..dea2db82e --- /dev/null +++ b/articles/language-generation/functions-injected-from-language-generation.md @@ -0,0 +1,257 @@ +--- +title: Functions injected from the language generation library - Bot Service +description: Describes how to inject functions from LG into templates. +keywords: functions from lg, reference, language generation +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Functions injected from the language generation library + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +The following article details how to inject functions from the [Language generation (LG)](../v4sdk/bot-builder-concept-language-generation.md) library. + +## ActivityAttachment + +Return an `activityAttachment` constructed from an object and a type. + +```lg +ActivityAttachment() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*content*> | Yes | object | Object containing the information of the attachment | +| <*type*> | Yes | string | A string representing the type of attachment | +||||| + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*activityAttachment*> | object | An `activityAttachment` formed from the inputs | +|||| + +*Example*: + +This example converts a collection of objects to an `activityAttachment`. + + +Suppose you have the following template: + +```lg +# externalHeroCardActivity(type, title, value) +[Activity + attachments = ${ActivityAttachment(json(fromFile('.\\herocard.json')), 'herocard')} +] +``` + +and the following `herocard.json`: + +```json +{ + "title": "titleContent", + "text": "textContent", + "Buttons": [ + { + "type": "imBack", + "Title": "titleContent", + "Value": "textContent", + "Text": "textContent" + } + ], + "tap": { + "type": "${type}", + "title": "${title}", + "text": "${title}", + "value": "${value}" + } +} +``` + +By calling `externalHeroCardActivity()` as a function: + +```lg +externalHeroCardActivity('signin', 'Signin Button', 'http://login.microsoft.com') +``` + +It returns a `herocard`: + +```lg +{ + "lgType" = "attachment", + "contenttype" = "herocard", + "content" = { + "title": "titleContent", + "text": "textContent", + "Buttons": [ + { + "type": "imBack", + "Title": "titleContent", + "Value": "textContent", + "Text": "textContent" + } + ], + "tap": { + "type": "signin", + "title": "Signin Button", + "text": "Signin Button", + "value": "http://login.microsoft.com" + } + } +} +``` + +## expandText + +Evaluate the plain text in an object and return the expanded text data. + +```lg +expandText() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*object*> | Yes | object | The object with text to expand. | +|||| + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*evaluated-result*> | object | The expanded text data. | +|||| + +*Example* + +This example evaluates the plain text in a JSON object and returns the expanded text result. + +Say you have the following object: + +```json +{ + "@answer": "hello ${user.name}", + "user": { + "name": "vivian" + } +} +``` + +Calling `expandText(@answer)` will result in the object **hello vivian**. + +## template + +Return the evaluated result of given template name and scope. + +```lg +template(, '', '', ...) +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*templateName*> | Yes | string | A string representing the template name | +| <*param1*>,<*param2*>, ... | Yes | Object | The parameters passed to the template | +||||| + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*evaluated-result*> | object | The result evaluated from the template as a function | +|||| + +*Example* + +This example evaluates the result of calling the template as a function. + +Suppose you have the following template: + +```lg +# welcome(userName) +- Hi ${userName} +- Hello ${userName} +- Hey ${userName} +``` + +Calling `template("welcome", "DL")` will result in one of the following: + +- _Hi DL_ +- _Hello DL_ +- _Hey DL_ + +## fromFile + +Return the evaluated result of the expression in the given file. + +```lg +fromFile() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*filePath*> | Yes | string | relative or absolute path of a file contains expressions | +||||| + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result*> | string | The string representation of the evaluated result | +|||| + +*Example* + +This example evaluates the result from the given file. + +Suppose you have a file called `/home/user/test.txt`. Inside the file there is the following: + +```lg +`you have ${add(1,2)} alarms` + +fromFile('/home/user/test.txt') +``` + +The `fromFile()` function will evaluate the expression and the result will replace the original expression. + +Calling `fromFile('/home/user/test.txt')` results in the string _you have 3 alarms_. + +## isTemplate + +Return whether a given template name is included in the evaluator. + +```lg +isTemplate() +``` + +| Parameter | Required | Type | Description | +| --------- | -------- | ---- | ----------- | +| <*templateName*> | Yes | String | A template name to check | +||||| + +| Return value | Type | Description | +| ------------ | -----| ----------- | +| <*result*> | Boolean | Whether the given template name is included in the evaluator | +|||| + +*Example* + +This example uses the `isTemplate()` function to check whether a given template name is in the evaluator. For example, here are three templates: + +```lg +# welcome +- hi + +# show-alarms +- 7:am and 8:pm + +# add-to-do +- you add a task at 7:pm +``` + +Calling `isTemplate("welcome")` will evaluate to `true`. Calling `isTemplate("delete-to-do")` will evaluate to `false`. + +## Additional Information + +- [.lg file format](../file-format/bot-builder-lg-file-format.md) +- [Structured response template](language-generation-structured-response-template.md) diff --git a/articles/language-generation/language-generation-API-reference.md b/articles/language-generation/language-generation-API-reference.md new file mode 100644 index 000000000..a312a770c --- /dev/null +++ b/articles/language-generation/language-generation-API-reference.md @@ -0,0 +1,25 @@ +--- +title: API reference for Language Generation - Bot Service +description: API reference for Language Generation library +keywords: language generation api, reference, language generation +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# API reference for language generation + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +## API reference links + +Language generation templates are language-agnostic. API references are available for bot developers working with language generation in the following languages: + +- [C#](/dotnet/api/microsoft.bot.builder.languagegeneration) +- [JavaScript](/javascript/api/botbuilder-lg) diff --git a/articles/language-generation/language-generation-structured-response-template.md b/articles/language-generation/language-generation-structured-response-template.md new file mode 100644 index 000000000..7c2501adb --- /dev/null +++ b/articles/language-generation/language-generation-structured-response-template.md @@ -0,0 +1,323 @@ +--- +title: Structured response template in Bot Framework SDK +description: Describe the structure response templates available with language generation. +keywords: structure response template, reference, language generation, lg +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Structured response template + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Structured response templates let developers define a complex structure that supports the extensive functionality of [Language generation (LG)](../v4sdk/bot-builder-concept-language-generation.md), like templating, composition, while leaving the interpretation of the structured response up to the caller of the LG library. + +For bot applications, the following support is provided: + +- [activity](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) definition +- [card](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md) definition + +The [Bot Framework activity](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) template includes several customizable fields. The following properties are the most commonly used and are configurable via an activity template definition: + +| Property | Use case | +|-------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| Text | Display text used by the channel to render visually | +| Speak | Spoken text used by the channel to render audibly | +| Attachments | List of attachments with their type. Used by channels to render as UI cards or other generic file attachment types. | +| SuggestedActions | List of actions rendered as suggestions to user. | +| InputHint | Controls audio capture stream state on devices that support spoken input. Possible values include `accepting`, `expecting`, or `ignoring`. | + +There is no default fallback behavior implemented by the template resolver. If a property isn't specified, then it remains unspecified. For example, the `Speak` property isn't automatically assigned to be the `Text` property if only the `Text` property is specified. + +## Definition + +Here's the definition of a structured template: + +```lg +# TemplateName +> this is a comment +[Structure-name + Property1 = .or. .or. + Property2 = list of values are denoted via '|'. e.g. a | b +> this is a comment about this specific property + Property3 = Nested structures are achieved through composition +] +``` + +Here's an example of a basic text template: + +```lg +# AskForAge.prompt +[Activity + Text = ${GetAge()} + Speak = ${GetAge()} +] + +# GetAge +- how old are you? +- what is your age? +``` + +Here's an example of text with a suggested action. Use **|** to denote a list. + +```lg +> With '|' you are making attachments a list. +# AskForAge.prompt +[Activity + Text = ${GetAge()} + SuggestedActions = 10 | 20 | 30 +] +``` + +Here's an example of a [Hero card](/microsoftteams/platform/task-modules-and-cards/cards/cards-reference#hero-card) definition: + +```lg +# HeroCard +[Herocard + title = Hero Card Example + subtitle = Microsoft Bot Framework + text = Build and connect intelligent bots to interact with your users naturally wherever they are, from text/sms to Skype, Slack, Office 365 mail and other popular services. + images = https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg + buttons = Option 1| Option 2| Option 3 +] +``` + +> [!NOTE] +> +> LG provides some variability in card definition, which is converted to align with the [SDK card definition](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md). For example, both `image` and `images` fields are supported in all the card definitions in LG even though only `images` are supported in the SDK card definition. +> +>The values defined in all of the `image` and `images` fields in a HeroCard or thumbnail card are combined and converted to an images list in the generated card. For the other types of cards, the last defined value in the template will be assigned to the `image` field. The values you assign to the `image/images` field can be a string, [adaptive expression](/azure/bot-service/bot-builder-concept-adaptive-expressions), or array in the format using **|**. + +Below is the combination of the previous templates: + +```lg +# AskForAge.prompt +[Activity + Text = ${GetAge()} + Speak = ${GetAge()} + Attachments = ${HeroCard()} + SuggestedActions = 10 | 20 | 30 + InputHint = expecting +] + +# GetAge +- how old are you? +- what is your age? + +# HeroCard +[Herocard + title = Hero Card Example + subtitle = Microsoft Bot Framework + text = Build and connect intelligent bots to interact with your users naturally wherever they are, from text/sms to Skype, Slack, Office 365 mail and other popular services. + images = https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg + buttons = Option 1| Option 2| Option 3 +] +``` + +By default any template reference is evaluated once during evaluation of a structured template. + +For example, `# AskForAge.prompt` returns the same resolution text for both the `Speak` and `Text` properties. + +```lg +# AskForAge.prompt +[Activity + Text = ${GetAge()} + Speak = ${GetAge()} +] + +# GetAge +- how old are you? +- what is your age? +``` + +You can use `!()` to request a new evaluation on each reference within a structured template. + +In the example below, `Speak` and `Text` may have different resolution text because `GetAge` is re-evaluated on each instance. + +```lg +[Activity + Text = ${GetAge()} + Speak = ${GetAge!()} +] + +# GetAge +- how old are you? +- what is your age? +``` + +Here's how to display a carousel of cards: + +```lg +# AskForAge.prompt +[Activity +> Defaults to carousel layout in case of list of cards + Attachments = ${foreach($cardValues, item, HeroCard(item)} +] + +# AskForAge.prompt_2 +[Activity +> Explicitly specify an attachment layout + Attachments = ${foreach($cardValues, item, HeroCard(item)} + AttachmentLayout = list +] + +# HeroCard (title, subtitle, text) +[Herocard + title = ${title} + subtitle = ${subtitle} + text = ${text} + images = https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg + buttons = Option 1| Option 2| Option 3 +] +``` + +Use **\\** as an escape character. + +```lg +> You can use '\' as an escape character +> \${GetAge()} would not be evaluated as expression, would be parsed as '${getAge()}' string +# AskForAge.prompt +[Activity + Text = \${GetAge()} + SuggestedActions = 10 \| cards | 20 \| cards +] +``` + +## Structured template composition + +The following composition behavior is supported with structured templates: + +- Composition is structure context-aware. If the target template being referred is also a structured template, then the structure type must match. For example, an ActivityTemplate can be referred to in another ActivityTemplate. +- References to simple or conditional response template can exist anywhere inside a structured template. + +Suppose you have the following template: + +```lg +# T1 +[Activity + Text = ${T2()} + Speak = foo bar ${T3().speak} +] + +# T2 +- This is awesome + +# T3 +[Activity + Speak = I can also speak! +] +``` + +A call to `evaluateTemplate('T1')` would result in the following internal structure: + +```lg +[Activity + Text = This is awesome + Speak = I can also speak! +] +``` + +## Full reference to another structured template + +You can include a reference to another structured template as a property in another structured template, or as a reference in another simple or conditional response template + +Here's an example of full reference to another structured template: + +```lg +# ST1 +[MyStruct + Text = foo + ${ST2()} +] +# ST2 +[MyStruct + Speak = bar +] +``` + +With this content, a call to `evaluateTemplate('ST1')` will result in the following internal structure: + +```lg +[MyStruct + Text = foo + Speak = bar +] +``` + +When the same property exists in both the calling template as well as the called template, the content in the caller will overwrite any content in the called template. + +Here's an example: + +```lg +# ST1 +[MyStruct + Text = foo + ${ST2()} +] +# ST2 +[MyStruct + Speak = bar + Text = zoo +] +``` + +With this context, a call to `evaluateTemplate('ST1')` will result in the following internal structure: + +```lg +[MyStruct + Text = foo + Speak = bar +] +``` + +Note that this style of composition can only exist at the root level. If there is a reference to another structured template within a property, then the resolution is contextual to that property. + +## External file reference in attachment structured + +There are two prebuilt functions used to externally reference files + +1. `fromFile(fileAbsoluteOrRelativePath)` loads a specified file. Content returned by this function will support evaluation of content. Template references, properties, and expressions are evaluated. +1. `ActivityAttachment(content, contentType)` sets the `contentType` if it's not already specified in the content. + +With these two prebuilt functions, you can pull in any externally defined content, including all card types. Use the following structured LG to compose an activity: + +```lg +# AdaptiveCard +[Activity + Attachments = ${ActivityAttachment(json(fromFile('../../card.json')), 'adaptiveCard')} +] + +# HeroCard +[Activity + Attachments = ${ActivityAttachment(json(fromFile('../../card.json')), 'heroCard')} +] +``` + +You can also use attachments, seen below: + +```lg +# AdaptiveCard +[Attachment + contenttype = adaptivecard + content = ${json(fromFile('../../card.json'))} +] + +# HeroCard +[Attachment + contenttype = herocard + content = ${json(fromFile('../../card.json'))} +] +``` + +## Additional Information + +- [C# API Reference](/dotnet/api/microsoft.bot.builder.languagegeneration) +- [JavaScript API reference](/javascript/api/botbuilder-lg) +- Read [Debug with Adaptive Tools](../bot-service-debug-adaptive-tools.md) to learn how to analyze and debug templates. diff --git a/articles/manage/TOC.md b/articles/manage/TOC.md deleted file mode 100644 index 6d243dc27..000000000 --- a/articles/manage/TOC.md +++ /dev/null @@ -1,36 +0,0 @@ -# [Manage a bot](../bot-service-manage-overview.md) -# [Bot analytics](../bot-service-manage-analytics.md) -# Channels -## [Connect a bot to channels](../bot-service-manage-channels.md) -## [Implement channel-specific functionality](../v4sdk/bot-builder-channeldata.md) -## [Cortana](../bot-service-channel-connect-cortana.md) -## Direct Line -### [About Direct Line](../bot-service-channel-directline.md) -### [Connect to Direct Line](../bot-service-channel-connect-directline.md) -### [Connect to Direct Line Speech](../bot-service-channel-connect-directlinespeech.md) -### [Direct Line App Service Extension](../bot-service-channel-directline-extension.md) -#### [Configure .NET bot for extension](../bot-service-channel-directline-extension-net-bot.md) -#### [Configure Node.js bot for extension](../bot-service-channel-directline-extension-node-bot.md) -#### [Create .NET Client with extension](../bot-service-channel-directline-extension-net-client.md) -#### [Use extension with WebChat](../bot-service-channel-directline-extension-webchat-client.md) -#### [Use extenson within VNET](../bot-service-channel-directline-extension-vnet.md) -## [Email](../bot-service-channel-connect-email.md) -## [Enable speech in Web Chat](../bot-service-channel-connect-webchat-speech.md) -## [Facebook](../bot-service-channel-connect-facebook.md) -## [GroupMe](../bot-service-channel-connect-groupme.md) -## [Kik](../bot-service-channel-connect-kik.md) -## [LINE](../bot-service-channel-connect-line.md) -## [Microsoft Teams](../channel-connect-teams.md) -## [Skype](../bot-service-channel-connect-skype.md) -## [Skype for Business](../bot-service-channel-connect-skypeforbusiness.md) -## [Slack](../bot-service-channel-connect-slack.md) -## [Telegram](../bot-service-channel-connect-telegram.md) -## [Twilio](../bot-service-channel-connect-twilio.md) -## [WeChat](../bot-service-channel-connect-wechat.md) -## [Web Chat](../bot-service-channel-connect-webchat.md) -## [Webex](../bot-service-adapter-connect-webex.md) -## [Additional channels](../bot-service-channel-additional-channels.md) -# [Configure bot settings](../bot-service-manage-settings.md) -# [Configure speech priming](../bot-service-manage-speech-priming.md) -# [Register a bot with Azure Bot Service](../bot-service-quickstart-registration.md) -# [Migrate your bot](../bot-service-migrate-bot.md) diff --git a/articles/media/Robot-clip-art-book-covers-feJCV3-clipart.png b/articles/media/Robot-clip-art-book-covers-feJCV3-clipart.png deleted file mode 100644 index 75929d462..000000000 Binary files a/articles/media/Robot-clip-art-book-covers-feJCV3-clipart.png and /dev/null differ diff --git a/articles/media/analytics-activities.png b/articles/media/analytics-activities.png deleted file mode 100644 index 72fab1e8a..000000000 Binary files a/articles/media/analytics-activities.png and /dev/null differ diff --git a/articles/media/analytics-channels.png b/articles/media/analytics-channels.png deleted file mode 100644 index ab876045d..000000000 Binary files a/articles/media/analytics-channels.png and /dev/null differ diff --git a/articles/media/analytics-enable.png b/articles/media/analytics-enable.png deleted file mode 100644 index febc48b66..000000000 Binary files a/articles/media/analytics-enable.png and /dev/null differ diff --git a/articles/media/analytics-messages.png b/articles/media/analytics-messages.png deleted file mode 100644 index d23ea3561..000000000 Binary files a/articles/media/analytics-messages.png and /dev/null differ diff --git a/articles/media/analytics-retention.png b/articles/media/analytics-retention.png deleted file mode 100644 index a0d22a86e..000000000 Binary files a/articles/media/analytics-retention.png and /dev/null differ diff --git a/articles/media/analytics-timepick.png b/articles/media/analytics-timepick.png deleted file mode 100644 index 2bd7b01b0..000000000 Binary files a/articles/media/analytics-timepick.png and /dev/null differ diff --git a/articles/media/analytics-users.png b/articles/media/analytics-users.png deleted file mode 100644 index 7487b6e79..000000000 Binary files a/articles/media/analytics-users.png and /dev/null differ diff --git a/articles/media/animation-card.png b/articles/media/animation-card.png deleted file mode 100644 index 505bb770a..000000000 Binary files a/articles/media/animation-card.png and /dev/null differ diff --git a/articles/media/app-registration/app-id.png b/articles/media/app-registration/app-id.png index f116e1d6c..e961146e5 100644 Binary files a/articles/media/app-registration/app-id.png and b/articles/media/app-registration/app-id.png differ diff --git a/articles/media/app-registration/create-app-id.png b/articles/media/app-registration/create-app-id.png index f4435a63d..322722670 100644 Binary files a/articles/media/app-registration/create-app-id.png and b/articles/media/app-registration/create-app-id.png differ diff --git a/articles/media/app-registration/new-registration.png b/articles/media/app-registration/new-registration.png index 8e5bc5f3c..f50d7d319 100644 Binary files a/articles/media/app-registration/new-registration.png and b/articles/media/app-registration/new-registration.png differ diff --git a/articles/media/app-registration/new-secret-copy.png b/articles/media/app-registration/new-secret-copy.png new file mode 100644 index 000000000..885bd1e17 Binary files /dev/null and b/articles/media/app-registration/new-secret-copy.png differ diff --git a/articles/media/app-registration/new-secret.png b/articles/media/app-registration/new-secret.png index c02c02bcb..660fd9c9c 100644 Binary files a/articles/media/app-registration/new-secret.png and b/articles/media/app-registration/new-secret.png differ diff --git a/articles/media/app-registration/registration-details.png b/articles/media/app-registration/registration-details.png index 8952e787e..676506181 100644 Binary files a/articles/media/app-registration/registration-details.png and b/articles/media/app-registration/registration-details.png differ diff --git a/articles/media/architecture/what-is-a-bot.png b/articles/media/architecture/what-is-a-bot.png new file mode 100644 index 000000000..6c3c94eca Binary files /dev/null and b/articles/media/architecture/what-is-a-bot.png differ diff --git a/articles/media/azure-bot-build/azure-csharp-publish.png b/articles/media/azure-bot-build/azure-csharp-publish.png deleted file mode 100644 index a24e9642d..000000000 Binary files a/articles/media/azure-bot-build/azure-csharp-publish.png and /dev/null differ diff --git a/articles/media/azure-bot-build/azure-deployment-github.png b/articles/media/azure-bot-build/azure-deployment-github.png deleted file mode 100644 index 673faa8dd..000000000 Binary files a/articles/media/azure-bot-build/azure-deployment-github.png and /dev/null differ diff --git a/articles/media/azure-bot-build/azure-deployment.png b/articles/media/azure-bot-build/azure-deployment.png deleted file mode 100644 index 55690d3fa..000000000 Binary files a/articles/media/azure-bot-build/azure-deployment.png and /dev/null differ diff --git a/articles/media/azure-bot-build/continuous-deployment-setup-github.png b/articles/media/azure-bot-build/continuous-deployment-setup-github.png deleted file mode 100644 index 4cf47d866..000000000 Binary files a/articles/media/azure-bot-build/continuous-deployment-setup-github.png and /dev/null differ diff --git a/articles/media/azure-bot-build/continuous-deployment-setup-vs-configuration.png b/articles/media/azure-bot-build/continuous-deployment-setup-vs-configuration.png deleted file mode 100644 index 23d2ca0fc..000000000 Binary files a/articles/media/azure-bot-build/continuous-deployment-setup-vs-configuration.png and /dev/null differ diff --git a/articles/media/azure-bot-build/continuous-deployment-setup-vs.png b/articles/media/azure-bot-build/continuous-deployment-setup-vs.png deleted file mode 100644 index 7221bd30b..000000000 Binary files a/articles/media/azure-bot-build/continuous-deployment-setup-vs.png and /dev/null differ diff --git a/articles/media/azure-bot-build/continuous-deployment-setup.png b/articles/media/azure-bot-build/continuous-deployment-setup.png deleted file mode 100644 index 63fec7749..000000000 Binary files a/articles/media/azure-bot-build/continuous-deployment-setup.png and /dev/null differ diff --git a/articles/media/azure-bot-build/cs-console-build-cmd.png b/articles/media/azure-bot-build/cs-console-build-cmd.png deleted file mode 100644 index cb3e580d5..000000000 Binary files a/articles/media/azure-bot-build/cs-console-build-cmd.png and /dev/null differ diff --git a/articles/media/azure-bot-build/cs-wwwroot-structure.png b/articles/media/azure-bot-build/cs-wwwroot-structure.png deleted file mode 100644 index babcb81ca..000000000 Binary files a/articles/media/azure-bot-build/cs-wwwroot-structure.png and /dev/null differ diff --git a/articles/media/azure-bot-build/functions-messages-code.png b/articles/media/azure-bot-build/functions-messages-code.png deleted file mode 100644 index 2141bc9bf..000000000 Binary files a/articles/media/azure-bot-build/functions-messages-code.png and /dev/null differ diff --git a/articles/media/azure-bot-build/node-wwwroot-structure.png b/articles/media/azure-bot-build/node-wwwroot-structure.png deleted file mode 100644 index 9437d0fda..000000000 Binary files a/articles/media/azure-bot-build/node-wwwroot-structure.png and /dev/null differ diff --git a/articles/media/azure-bot-build/open-online-code-editor-restart-appservice.png b/articles/media/azure-bot-build/open-online-code-editor-restart-appservice.png deleted file mode 100644 index 38c8e117a..000000000 Binary files a/articles/media/azure-bot-build/open-online-code-editor-restart-appservice.png and /dev/null differ diff --git a/articles/media/azure-bot-build/open-online-code-editor.png b/articles/media/azure-bot-build/open-online-code-editor.png deleted file mode 100644 index d25ceb55a..000000000 Binary files a/articles/media/azure-bot-build/open-online-code-editor.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/abs-create-blade.png b/articles/media/azure-bot-quickstarts/abs-create-blade.png deleted file mode 100644 index d45286a1f..000000000 Binary files a/articles/media/azure-bot-quickstarts/abs-create-blade.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/abs-keys-download.png b/articles/media/azure-bot-quickstarts/abs-keys-download.png deleted file mode 100644 index 4fc6c59e3..000000000 Binary files a/articles/media/azure-bot-quickstarts/abs-keys-download.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/azure-webchat-test.png b/articles/media/azure-bot-quickstarts/azure-webchat-test.png deleted file mode 100644 index 46e12ec4f..000000000 Binary files a/articles/media/azure-bot-quickstarts/azure-webchat-test.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project-vs2019.png b/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project-vs2019.png deleted file mode 100644 index 8d9ceb493..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project-vs2019.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project.png b/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project.png deleted file mode 100644 index 6ca0ed968..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-builder-dotnet-project.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-complete.PNG b/articles/media/azure-bot-quickstarts/bot-channels-registration-app-complete.PNG deleted file mode 100644 index 1b4c02ac6..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-complete.PNG and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets-create.PNG b/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets-create.PNG deleted file mode 100644 index 74d01be14..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets-create.PNG and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets.PNG b/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets.PNG deleted file mode 100644 index 352963313..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-secrets.PNG and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-settings.PNG b/articles/media/azure-bot-quickstarts/bot-channels-registration-app-settings.PNG deleted file mode 100644 index bbe556254..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration-app-settings.PNG and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration-app.png b/articles/media/azure-bot-quickstarts/bot-channels-registration-app.png deleted file mode 100644 index f59783cce..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration-app.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-channels-registration.png b/articles/media/azure-bot-quickstarts/bot-channels-registration.png deleted file mode 100644 index 97d7bfd0f..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-channels-registration.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/bot-web-app-registration.png b/articles/media/azure-bot-quickstarts/bot-web-app-registration.png deleted file mode 100644 index 1447bfea9..000000000 Binary files a/articles/media/azure-bot-quickstarts/bot-web-app-registration.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/echobot-js.png b/articles/media/azure-bot-quickstarts/echobot-js.png deleted file mode 100644 index 95866ee85..000000000 Binary files a/articles/media/azure-bot-quickstarts/echobot-js.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/function-create-bot-service-blade.png b/articles/media/azure-bot-quickstarts/function-create-bot-service-blade.png deleted file mode 100644 index faa08bfd8..000000000 Binary files a/articles/media/azure-bot-quickstarts/function-create-bot-service-blade.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-app-service.png b/articles/media/azure-bot-quickstarts/getting-started-app-service.png deleted file mode 100644 index aee65cc65..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-app-service.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-app-settings-1.png b/articles/media/azure-bot-quickstarts/getting-started-app-settings-1.png deleted file mode 100644 index 10cbb0454..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-app-settings-1.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-app-settings-2.png b/articles/media/azure-bot-quickstarts/getting-started-app-settings-2.png deleted file mode 100644 index cd8535dc5..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-app-settings-2.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-bot-registration.png b/articles/media/azure-bot-quickstarts/getting-started-bot-registration.png deleted file mode 100644 index ad55f20d4..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-bot-registration.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-msa.png b/articles/media/azure-bot-quickstarts/getting-started-msa.png deleted file mode 100644 index e6f5adef2..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-msa.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-publish-main.png b/articles/media/azure-bot-quickstarts/getting-started-publish-main.png deleted file mode 100644 index 143092d75..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-publish-main.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-publish-setting.png b/articles/media/azure-bot-quickstarts/getting-started-publish-setting.png deleted file mode 100644 index e60e7d9b0..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-publish-setting.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/getting-started-test-webchat.png b/articles/media/azure-bot-quickstarts/getting-started-test-webchat.png deleted file mode 100644 index 13a91303a..000000000 Binary files a/articles/media/azure-bot-quickstarts/getting-started-test-webchat.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/notification-deployment-succeeded.png b/articles/media/azure-bot-quickstarts/notification-deployment-succeeded.png deleted file mode 100644 index 5b6c3cdf8..000000000 Binary files a/articles/media/azure-bot-quickstarts/notification-deployment-succeeded.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/notification-registration-deployment-succeeded.png b/articles/media/azure-bot-quickstarts/notification-registration-deployment-succeeded.png deleted file mode 100644 index b1151e203..000000000 Binary files a/articles/media/azure-bot-quickstarts/notification-registration-deployment-succeeded.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/registration-create-bot-service-blade.png b/articles/media/azure-bot-quickstarts/registration-create-bot-service-blade.png deleted file mode 100644 index e0f47bd21..000000000 Binary files a/articles/media/azure-bot-quickstarts/registration-create-bot-service-blade.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/sdk-bot-cs-bot-templates.png b/articles/media/azure-bot-quickstarts/sdk-bot-cs-bot-templates.png deleted file mode 100644 index 9caa06751..000000000 Binary files a/articles/media/azure-bot-quickstarts/sdk-bot-cs-bot-templates.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/sdk-create-bot-service-blade.png b/articles/media/azure-bot-quickstarts/sdk-create-bot-service-blade.png deleted file mode 100644 index f226bf3a3..000000000 Binary files a/articles/media/azure-bot-quickstarts/sdk-create-bot-service-blade.png and /dev/null differ diff --git a/articles/media/azure-bot-quickstarts/test-in-web-chat-control.png b/articles/media/azure-bot-quickstarts/test-in-web-chat-control.png deleted file mode 100644 index 04fb866d2..000000000 Binary files a/articles/media/azure-bot-quickstarts/test-in-web-chat-control.png and /dev/null differ diff --git a/articles/media/azure-bot-resource/azure-bot-ms-app-id.png b/articles/media/azure-bot-resource/azure-bot-ms-app-id.png new file mode 100644 index 000000000..30dcd717d Binary files /dev/null and b/articles/media/azure-bot-resource/azure-bot-ms-app-id.png differ diff --git a/articles/media/azure-bot-resource/azure-bot-ms-app-st.png b/articles/media/azure-bot-resource/azure-bot-ms-app-st.png new file mode 100644 index 000000000..73908d76d Binary files /dev/null and b/articles/media/azure-bot-resource/azure-bot-ms-app-st.png differ diff --git a/articles/media/azure-bot-resource/azure-bot-project-details.png b/articles/media/azure-bot-resource/azure-bot-project-details.png new file mode 100644 index 000000000..b8c207400 Binary files /dev/null and b/articles/media/azure-bot-resource/azure-bot-project-details.png differ diff --git a/articles/media/azure-bot-service-bf-portal.png b/articles/media/azure-bot-service-bf-portal.png deleted file mode 100644 index 48afd2c6f..000000000 Binary files a/articles/media/azure-bot-service-bf-portal.png and /dev/null differ diff --git a/articles/media/azure-bot-service-coding-language.png b/articles/media/azure-bot-service-coding-language.png deleted file mode 100644 index 25b04eb0e..000000000 Binary files a/articles/media/azure-bot-service-coding-language.png and /dev/null differ diff --git a/articles/media/azure-bot-service-console-icon.png b/articles/media/azure-bot-service-console-icon.png deleted file mode 100644 index 0af4130e8..000000000 Binary files a/articles/media/azure-bot-service-console-icon.png and /dev/null differ diff --git a/articles/media/azure-bot-service-create-app-id.png b/articles/media/azure-bot-service-create-app-id.png deleted file mode 100644 index dceb3905f..000000000 Binary files a/articles/media/azure-bot-service-create-app-id.png and /dev/null differ diff --git a/articles/media/azure-bot-service-create-bot.png b/articles/media/azure-bot-service-create-bot.png deleted file mode 100644 index 8f42102a8..000000000 Binary files a/articles/media/azure-bot-service-create-bot.png and /dev/null differ diff --git a/articles/media/azure-bot-service-editor.png b/articles/media/azure-bot-service-editor.png deleted file mode 100644 index 9971a00e2..000000000 Binary files a/articles/media/azure-bot-service-editor.png and /dev/null differ diff --git a/articles/media/azure-bot-service-first-bot-notification.png b/articles/media/azure-bot-service-first-bot-notification.png deleted file mode 100644 index cca48a7e6..000000000 Binary files a/articles/media/azure-bot-service-first-bot-notification.png and /dev/null differ diff --git a/articles/media/azure-bot-service-password.png b/articles/media/azure-bot-service-password.png deleted file mode 100644 index 0cc744fcd..000000000 Binary files a/articles/media/azure-bot-service-password.png and /dev/null differ diff --git a/articles/media/azure-bot-service-template.png b/articles/media/azure-bot-service-template.png deleted file mode 100644 index cb8447971..000000000 Binary files a/articles/media/azure-bot-service-template.png and /dev/null differ diff --git a/articles/media/azure-browse.png b/articles/media/azure-browse.png deleted file mode 100644 index 1d9387e57..000000000 Binary files a/articles/media/azure-browse.png and /dev/null differ diff --git a/articles/media/azure-create-webapp.png b/articles/media/azure-create-webapp.png deleted file mode 100644 index 7089086c8..000000000 Binary files a/articles/media/azure-create-webapp.png and /dev/null differ diff --git a/articles/media/azure-deployment.png b/articles/media/azure-deployment.png deleted file mode 100644 index f02b40110..000000000 Binary files a/articles/media/azure-deployment.png and /dev/null differ diff --git a/articles/media/azure-manage-a-bot/app-service-settings.png b/articles/media/azure-manage-a-bot/app-service-settings.png deleted file mode 100644 index 939b64043..000000000 Binary files a/articles/media/azure-manage-a-bot/app-service-settings.png and /dev/null differ diff --git a/articles/media/azure-manage-a-bot/app-settings.png b/articles/media/azure-manage-a-bot/app-settings.png deleted file mode 100644 index 29d63db47..000000000 Binary files a/articles/media/azure-manage-a-bot/app-settings.png and /dev/null differ diff --git a/articles/media/azure-manage-a-bot/azure-bot-create-sdk.png b/articles/media/azure-manage-a-bot/azure-bot-create-sdk.png new file mode 100644 index 000000000..9807b6856 Binary files /dev/null and b/articles/media/azure-manage-a-bot/azure-bot-create-sdk.png differ diff --git a/articles/media/azure-manage-a-bot/azure-bot-resource.png b/articles/media/azure-manage-a-bot/azure-bot-resource.png new file mode 100644 index 000000000..0a45989a9 Binary files /dev/null and b/articles/media/azure-manage-a-bot/azure-bot-resource.png differ diff --git a/articles/media/azure-manage-a-bot/bot-management.png b/articles/media/azure-manage-a-bot/bot-management.png deleted file mode 100644 index b021bb152..000000000 Binary files a/articles/media/azure-manage-a-bot/bot-management.png and /dev/null differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-cred.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-cred.png new file mode 100644 index 000000000..615629be2 Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-cred.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-mi.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-mi.png new file mode 100644 index 000000000..9ab4b3880 Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-mi.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others-account.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others-account.png new file mode 100644 index 000000000..76ded816f Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others-account.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others.png new file mode 100644 index 000000000..489905aba Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-others.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select-search.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select-search.png new file mode 100644 index 000000000..f84426613 Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select-search.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select.png b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select.png new file mode 100644 index 000000000..7c3d03fa5 Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds-scenario-select.png differ diff --git a/articles/media/azure-manage-a-bot/entra-fic-creds.png b/articles/media/azure-manage-a-bot/entra-fic-creds.png new file mode 100644 index 000000000..26aa3eec0 Binary files /dev/null and b/articles/media/azure-manage-a-bot/entra-fic-creds.png differ diff --git a/articles/media/azure-manage-a-bot/managed-identity.png b/articles/media/azure-manage-a-bot/managed-identity.png new file mode 100644 index 000000000..3db42acf6 Binary files /dev/null and b/articles/media/azure-manage-a-bot/managed-identity.png differ diff --git a/articles/media/azure-manage-a-bot/overview.png b/articles/media/azure-manage-a-bot/overview.png deleted file mode 100644 index 83e5d83cc..000000000 Binary files a/articles/media/azure-manage-a-bot/overview.png and /dev/null differ diff --git a/articles/media/azure-manage-a-bot/user-mi-create.png b/articles/media/azure-manage-a-bot/user-mi-create.png new file mode 100644 index 000000000..88adbd951 Binary files /dev/null and b/articles/media/azure-manage-a-bot/user-mi-create.png differ diff --git a/articles/media/azure-secrets.png b/articles/media/azure-secrets.png deleted file mode 100644 index 5e0d1f5e5..000000000 Binary files a/articles/media/azure-secrets.png and /dev/null differ diff --git a/articles/media/azure_publish1.png b/articles/media/azure_publish1.png deleted file mode 100644 index eea2723f2..000000000 Binary files a/articles/media/azure_publish1.png and /dev/null differ diff --git a/articles/media/azure_publish2.png b/articles/media/azure_publish2.png deleted file mode 100644 index 4301d6d12..000000000 Binary files a/articles/media/azure_publish2.png and /dev/null differ diff --git a/articles/media/azure_publish3.png b/articles/media/azure_publish3.png deleted file mode 100644 index df9b2282c..000000000 Binary files a/articles/media/azure_publish3.png and /dev/null differ diff --git a/articles/media/bot-builder-basics/how-bots-work.png b/articles/media/bot-builder-basics/how-bots-work.png new file mode 100644 index 000000000..8f3f17ed0 Binary files /dev/null and b/articles/media/bot-builder-basics/how-bots-work.png differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-builder-dotnet-luis-message-flow-bot-code-notes.png b/articles/media/bot-builder-dotnet-use-luis/bot-builder-dotnet-luis-message-flow-bot-code-notes.png deleted file mode 100644 index db92a5bb2..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-builder-dotnet-luis-message-flow-bot-code-notes.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-app-settings.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-app-settings.png deleted file mode 100644 index 533ec9318..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-app-settings.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-build.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-build.png deleted file mode 100644 index 323abbfe5..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-build.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-create-notification.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-create-notification.png deleted file mode 100644 index 82f970e40..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-create-notification.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-creation.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-creation.png deleted file mode 100644 index b92a6f145..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-creation.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-run-console.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-run-console.png deleted file mode 100644 index 06d721b16..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-run-console.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-selection.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-selection.png deleted file mode 100644 index b92c372b0..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-selection.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-appname.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-appname.png deleted file mode 100644 index d7476bf67..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-appname.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-template.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-template.png deleted file mode 100644 index a71072d58..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-setting-callout-template.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-test-notebot.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-test-notebot.png deleted file mode 100644 index f29db83e8..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-test-notebot.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/bot-service-web-chat.png b/articles/media/bot-builder-dotnet-use-luis/bot-service-web-chat.png deleted file mode 100644 index 5d0baa8b2..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/bot-service-web-chat.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/dotnet-notes-sample-emulator.png b/articles/media/bot-builder-dotnet-use-luis/dotnet-notes-sample-emulator.png deleted file mode 100644 index 510c11d9e..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/dotnet-notes-sample-emulator.png and /dev/null differ diff --git a/articles/media/bot-builder-dotnet-use-luis/luis-intent-list.png b/articles/media/bot-builder-dotnet-use-luis/luis-intent-list.png deleted file mode 100644 index 7894ba16a..000000000 Binary files a/articles/media/bot-builder-dotnet-use-luis/luis-intent-list.png and /dev/null differ diff --git a/articles/media/bot-builder-howto-answer-questions/flow-diagram-cs.png b/articles/media/bot-builder-howto-answer-questions/flow-diagram-cs.png new file mode 100644 index 000000000..da22f9e2a Binary files /dev/null and b/articles/media/bot-builder-howto-answer-questions/flow-diagram-cs.png differ diff --git a/articles/media/bot-builder-howto-answer-questions/flow-diagram-js.png b/articles/media/bot-builder-howto-answer-questions/flow-diagram-js.png new file mode 100644 index 000000000..d6d12490d Binary files /dev/null and b/articles/media/bot-builder-howto-answer-questions/flow-diagram-js.png differ diff --git a/articles/media/bot-builder-nodejs-dialog-manage-conversation/waterfall-results.png b/articles/media/bot-builder-nodejs-dialog-manage-conversation/waterfall-results.png deleted file mode 100644 index 53c959d9d..000000000 Binary files a/articles/media/bot-builder-nodejs-dialog-manage-conversation/waterfall-results.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-state-azure-table-storage/bot-builder-nodejs-state-azure-table-storage-query.png b/articles/media/bot-builder-nodejs-state-azure-table-storage/bot-builder-nodejs-state-azure-table-storage-query.png deleted file mode 100644 index 97d5858c0..000000000 Binary files a/articles/media/bot-builder-nodejs-state-azure-table-storage/bot-builder-nodejs-state-azure-table-storage-query.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-luis-message-flow-bot-code-notes.png b/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-luis-message-flow-bot-code-notes.png deleted file mode 100644 index 44c4c37f3..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-luis-message-flow-bot-code-notes.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-interruption.png b/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-interruption.png deleted file mode 100644 index b5397983b..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-interruption.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-output.png b/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-output.png deleted file mode 100644 index 6636aca48..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-create-note-output.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-read-note-output.png b/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-read-note-output.png deleted file mode 100644 index e13ee97ac..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-builder-nodejs-use-luis-read-note-output.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-app-settings.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-app-settings.png deleted file mode 100644 index 533ec9318..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-app-settings.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-build.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-build.png deleted file mode 100644 index 323abbfe5..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-build.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-creation.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-creation.png deleted file mode 100644 index b92a6f145..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-creation.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-selection.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-selection.png deleted file mode 100644 index b92c372b0..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-selection.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-appname.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-appname.png deleted file mode 100644 index d7476bf67..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-appname.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-template.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-template.png deleted file mode 100644 index 72aad459d..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-setting-callout-template.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-test-notebot.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-test-notebot.png deleted file mode 100644 index 6a742d053..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-test-notebot.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/bot-service-web-chat.png b/articles/media/bot-builder-nodejs-use-luis/bot-service-web-chat.png deleted file mode 100644 index 5d0baa8b2..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/bot-service-web-chat.png and /dev/null differ diff --git a/articles/media/bot-builder-nodejs-use-luis/luis-intent-list.png b/articles/media/bot-builder-nodejs-use-luis/luis-intent-list.png deleted file mode 100644 index 7894ba16a..000000000 Binary files a/articles/media/bot-builder-nodejs-use-luis/luis-intent-list.png and /dev/null differ diff --git a/articles/media/bot-builder-state-data-db/cosmosdb.png b/articles/media/bot-builder-state-data-db/cosmosdb.png deleted file mode 100644 index 4de7bce46..000000000 Binary files a/articles/media/bot-builder-state-data-db/cosmosdb.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/az-browser-login.png b/articles/media/bot-builder-tools/az-browser-login.png deleted file mode 100644 index 455cf853b..000000000 Binary files a/articles/media/bot-builder-tools/az-browser-login.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/az-cli-bot.png b/articles/media/bot-builder-tools/az-cli-bot.png deleted file mode 100644 index d3b4141d6..000000000 Binary files a/articles/media/bot-builder-tools/az-cli-bot.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/az-echo-bot.png b/articles/media/bot-builder-tools/az-echo-bot.png deleted file mode 100644 index b55af3a5f..000000000 Binary files a/articles/media/bot-builder-tools/az-echo-bot.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/az-login-command.png b/articles/media/bot-builder-tools/az-login-command.png deleted file mode 100644 index 60408f00a..000000000 Binary files a/articles/media/bot-builder-tools/az-login-command.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/az-portal-manage-code.png b/articles/media/bot-builder-tools/az-portal-manage-code.png deleted file mode 100644 index 38cf731c3..000000000 Binary files a/articles/media/bot-builder-tools/az-portal-manage-code.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/cli-bot-download-command.png b/articles/media/bot-builder-tools/cli-bot-download-command.png deleted file mode 100644 index 5f59480af..000000000 Binary files a/articles/media/bot-builder-tools/cli-bot-download-command.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/cli-bot-download.png b/articles/media/bot-builder-tools/cli-bot-download.png deleted file mode 100644 index cab2046b7..000000000 Binary files a/articles/media/bot-builder-tools/cli-bot-download.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/ludown-luis-create.png b/articles/media/bot-builder-tools/ludown-luis-create.png deleted file mode 100644 index a859f781d..000000000 Binary files a/articles/media/bot-builder-tools/ludown-luis-create.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/ludown-options.png b/articles/media/bot-builder-tools/ludown-options.png deleted file mode 100644 index c53332428..000000000 Binary files a/articles/media/bot-builder-tools/ludown-options.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/ludown-qna-create.png b/articles/media/bot-builder-tools/ludown-qna-create.png deleted file mode 100644 index a17f6008d..000000000 Binary files a/articles/media/bot-builder-tools/ludown-qna-create.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/luis-init.png b/articles/media/bot-builder-tools/luis-init.png deleted file mode 100644 index 279972aa3..000000000 Binary files a/articles/media/bot-builder-tools/luis-init.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/ms-device-login.png b/articles/media/bot-builder-tools/ms-device-login.png deleted file mode 100644 index 26bc02356..000000000 Binary files a/articles/media/bot-builder-tools/ms-device-login.png and /dev/null differ diff --git a/articles/media/bot-builder-tools/qnamaker-init.png b/articles/media/bot-builder-tools/qnamaker-init.png deleted file mode 100644 index c30314167..000000000 Binary files a/articles/media/bot-builder-tools/qnamaker-init.png and /dev/null differ diff --git a/articles/media/bot-connector-service-slide.png b/articles/media/bot-connector-service-slide.png deleted file mode 100644 index 851962d9d..000000000 Binary files a/articles/media/bot-connector-service-slide.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-devtunnel.png b/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-devtunnel.png new file mode 100644 index 000000000..64b8c8529 Binary files /dev/null and b/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-devtunnel.png differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-ngrok.png b/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-ngrok.png deleted file mode 100644 index 203a9c8a6..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-channels-setting-ngrok.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-check-emulator-version.png b/articles/media/bot-debug-inspection-middleware/bot-debug-check-emulator-version.png deleted file mode 100644 index 92d91f31e..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-check-emulator-version.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-debugmode-ngrok-emulator.png b/articles/media/bot-debug-inspection-middleware/bot-debug-debugmode-ngrok-emulator.png deleted file mode 100644 index 81b77cb40..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-debugmode-ngrok-emulator.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-emulator-values.png b/articles/media/bot-debug-inspection-middleware/bot-debug-emulator-values.png deleted file mode 100644 index 1e529b7ae..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-emulator-values.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png b/articles/media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png index b35e37e76..72a3437ba 100644 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png and b/articles/media/bot-debug-inspection-middleware/bot-debug-get-inputs-id-secret.png differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-get-latest-version.png b/articles/media/bot-debug-inspection-middleware/bot-debug-get-latest-version.png deleted file mode 100644 index 514ea8633..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-get-latest-version.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-inspector-middleware-result.gif b/articles/media/bot-debug-inspection-middleware/bot-debug-inspector-middleware-result.gif deleted file mode 100644 index c3df5ca48..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-inspector-middleware-result.gif and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-ngrok.png b/articles/media/bot-debug-inspection-middleware/bot-debug-ngrok.png deleted file mode 100644 index 6a7a7152b..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-ngrok.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-notification.png b/articles/media/bot-debug-inspection-middleware/bot-debug-notification.png deleted file mode 100644 index b3468b0ef..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-notification.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-run-bot.png b/articles/media/bot-debug-inspection-middleware/bot-debug-run-bot.png deleted file mode 100644 index e1428dc28..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-run-bot.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-run-ngrok.png b/articles/media/bot-debug-inspection-middleware/bot-debug-run-ngrok.png deleted file mode 100644 index b45eca114..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-run-ngrok.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-test-webchat.png b/articles/media/bot-debug-inspection-middleware/bot-debug-test-webchat.png deleted file mode 100644 index 8332ebbd6..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-test-webchat.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-update-download.png b/articles/media/bot-debug-inspection-middleware/bot-debug-update-download.png deleted file mode 100644 index 0185cf6e0..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-update-download.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-uuid-success.png b/articles/media/bot-debug-inspection-middleware/bot-debug-uuid-success.png deleted file mode 100644 index 048cf9d22..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-uuid-success.png and /dev/null differ diff --git a/articles/media/bot-debug-inspection-middleware/bot-debug-uuid.png b/articles/media/bot-debug-inspection-middleware/bot-debug-uuid.png deleted file mode 100644 index 8f59c1fd1..000000000 Binary files a/articles/media/bot-debug-inspection-middleware/bot-debug-uuid.png and /dev/null differ diff --git a/articles/media/bot-framework-devops/SourceWebAppsBotFramework.png b/articles/media/bot-framework-devops/SourceWebAppsBotFramework.png deleted file mode 100644 index 793f6c0e0..000000000 Binary files a/articles/media/bot-framework-devops/SourceWebAppsBotFramework.png and /dev/null differ diff --git a/articles/media/bot-proactive-diagram.png b/articles/media/bot-proactive-diagram.png deleted file mode 100644 index f722e6d48..000000000 Binary files a/articles/media/bot-proactive-diagram.png and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-slack/event-subscriptions.PNG b/articles/media/bot-service-adapter-connect-slack/event-subscriptions.PNG deleted file mode 100644 index e4684eb7c..000000000 Binary files a/articles/media/bot-service-adapter-connect-slack/event-subscriptions.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-slack/redirect-url.PNG b/articles/media/bot-service-adapter-connect-slack/redirect-url.PNG deleted file mode 100644 index 0ca0cb37e..000000000 Binary files a/articles/media/bot-service-adapter-connect-slack/redirect-url.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-slack/slack-tokens.PNG b/articles/media/bot-service-adapter-connect-slack/slack-tokens.PNG deleted file mode 100644 index aeced4d77..000000000 Binary files a/articles/media/bot-service-adapter-connect-slack/slack-tokens.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-webex/create-bot-settings.PNG b/articles/media/bot-service-adapter-connect-webex/create-bot-settings.PNG deleted file mode 100644 index f0160863e..000000000 Binary files a/articles/media/bot-service-adapter-connect-webex/create-bot-settings.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-webex/create-bot.PNG b/articles/media/bot-service-adapter-connect-webex/create-bot.PNG deleted file mode 100644 index 48993fbf3..000000000 Binary files a/articles/media/bot-service-adapter-connect-webex/create-bot.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-webex/webex-contact-person.PNG b/articles/media/bot-service-adapter-connect-webex/webex-contact-person.PNG deleted file mode 100644 index 3e52ab936..000000000 Binary files a/articles/media/bot-service-adapter-connect-webex/webex-contact-person.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-webex/webex-webhook-form.PNG b/articles/media/bot-service-adapter-connect-webex/webex-webhook-form.PNG deleted file mode 100644 index af2d68caf..000000000 Binary files a/articles/media/bot-service-adapter-connect-webex/webex-webhook-form.PNG and /dev/null differ diff --git a/articles/media/bot-service-adapter-connect-webex/webex-webhook-put-endpoint.PNG b/articles/media/bot-service-adapter-connect-webex/webex-webhook-put-endpoint.PNG deleted file mode 100644 index 16ffd3800..000000000 Binary files a/articles/media/bot-service-adapter-connect-webex/webex-webhook-put-endpoint.PNG and /dev/null differ diff --git a/articles/media/bot-service-build-continuous-deployment/cicd-configured.png b/articles/media/bot-service-build-continuous-deployment/cicd-configured.png new file mode 100644 index 000000000..eefb09090 Binary files /dev/null and b/articles/media/bot-service-build-continuous-deployment/cicd-configured.png differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-addchannel.png b/articles/media/bot-service-channel-connect-directline/directline-addchannel.png deleted file mode 100644 index 189640687..000000000 Binary files a/articles/media/bot-service-channel-connect-directline/directline-addchannel.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-addsite.png b/articles/media/bot-service-channel-connect-directline/directline-addsite.png deleted file mode 100644 index 887400727..000000000 Binary files a/articles/media/bot-service-channel-connect-directline/directline-addsite.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-configure-site.png b/articles/media/bot-service-channel-connect-directline/directline-configure-site.png new file mode 100644 index 000000000..2995e55a0 Binary files /dev/null and b/articles/media/bot-service-channel-connect-directline/directline-configure-site.png differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-copykey.png b/articles/media/bot-service-channel-connect-directline/directline-copykey.png deleted file mode 100644 index d3ac1bf75..000000000 Binary files a/articles/media/bot-service-channel-connect-directline/directline-copykey.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-enhanced-authentication.png b/articles/media/bot-service-channel-connect-directline/directline-enhanced-authentication.png new file mode 100644 index 000000000..e4cf22c2f Binary files /dev/null and b/articles/media/bot-service-channel-connect-directline/directline-enhanced-authentication.png differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-newsite.png b/articles/media/bot-service-channel-connect-directline/directline-newsite.png new file mode 100644 index 000000000..da7dbea03 Binary files /dev/null and b/articles/media/bot-service-channel-connect-directline/directline-newsite.png differ diff --git a/articles/media/bot-service-channel-connect-directline/directline-showkey.png b/articles/media/bot-service-channel-connect-directline/directline-showkey.png index 5c5c2aabc..816283d4d 100644 Binary files a/articles/media/bot-service-channel-connect-directline/directline-showkey.png and b/articles/media/bot-service-channel-connect-directline/directline-showkey.png differ diff --git a/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-basic-authentication.png b/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-basic-authentication.png new file mode 100644 index 000000000..0a25b40e7 Binary files /dev/null and b/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-basic-authentication.png differ diff --git a/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-email-credentials.png b/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-email-credentials.png deleted file mode 100644 index ef5569bff..000000000 Binary files a/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-email-credentials.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-modern-authentication.png b/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-modern-authentication.png new file mode 100644 index 000000000..1be0583c8 Binary files /dev/null and b/articles/media/bot-service-channel-connect-email/bot-service-channel-connect-modern-authentication.png differ diff --git a/articles/media/bot-service-channel-connect-email/bot-service-channel-validation-code.png b/articles/media/bot-service-channel-connect-email/bot-service-channel-validation-code.png new file mode 100644 index 000000000..10df312a1 Binary files /dev/null and b/articles/media/bot-service-channel-connect-email/bot-service-channel-validation-code.png differ diff --git a/articles/media/bot-service-channel-connect-facebook/add-button.PNG b/articles/media/bot-service-channel-connect-facebook/add-button.PNG deleted file mode 100644 index 86aee2805..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/add-button.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/add-product.PNG b/articles/media/bot-service-channel-connect-facebook/add-product.PNG deleted file mode 100644 index dee37ecbf..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/add-product.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/api_settings.png b/articles/media/bot-service-channel-connect-facebook/api_settings.png deleted file mode 100644 index cb6c8b27c..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/api_settings.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/app-name.PNG b/articles/media/bot-service-channel-connect-facebook/app-name.PNG deleted file mode 100644 index 10dd7fe78..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/app-name.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/app-page-permissions.PNG b/articles/media/bot-service-channel-connect-facebook/app-page-permissions.PNG deleted file mode 100644 index 509e6ebb5..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/app-page-permissions.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/button-settings-2.PNG b/articles/media/bot-service-channel-connect-facebook/button-settings-2.PNG deleted file mode 100644 index c6b70e7d8..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/button-settings-2.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/button-settings.PNG b/articles/media/bot-service-channel-connect-facebook/button-settings.PNG deleted file mode 100644 index 51a23d7f8..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/button-settings.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/create-app-button.PNG b/articles/media/bot-service-channel-connect-facebook/create-app-button.PNG deleted file mode 100644 index c3fe35e62..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/create-app-button.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/create-page.PNG b/articles/media/bot-service-channel-connect-facebook/create-page.PNG deleted file mode 100644 index f536562b8..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/create-page.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/edit-callback-url.PNG b/articles/media/bot-service-channel-connect-facebook/edit-callback-url.PNG deleted file mode 100644 index 873db63cd..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/edit-callback-url.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/subscribe-webhook.png b/articles/media/bot-service-channel-connect-facebook/subscribe-webhook.png deleted file mode 100644 index 7081d8bc8..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/subscribe-webhook.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-facebook/webhook-subscriptions.PNG b/articles/media/bot-service-channel-connect-facebook/webhook-subscriptions.PNG deleted file mode 100644 index a3be06899..000000000 Binary files a/articles/media/bot-service-channel-connect-facebook/webhook-subscriptions.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-twilio/get-phone-number.PNG b/articles/media/bot-service-channel-connect-twilio/get-phone-number.PNG deleted file mode 100644 index dd25d6a39..000000000 Binary files a/articles/media/bot-service-channel-connect-twilio/get-phone-number.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-twilio/get-started-voice.png b/articles/media/bot-service-channel-connect-twilio/get-started-voice.png deleted file mode 100644 index c80ac9048..000000000 Binary files a/articles/media/bot-service-channel-connect-twilio/get-started-voice.png and /dev/null differ diff --git a/articles/media/bot-service-channel-connect-twilio/twilio-number-messaging-settings.PNG b/articles/media/bot-service-channel-connect-twilio/twilio-number-messaging-settings.PNG deleted file mode 100644 index fd63a2a36..000000000 Binary files a/articles/media/bot-service-channel-connect-twilio/twilio-number-messaging-settings.PNG and /dev/null differ diff --git a/articles/media/bot-service-channel-inspector.png b/articles/media/bot-service-channel-inspector.png deleted file mode 100644 index 2bf6fe1dc..000000000 Binary files a/articles/media/bot-service-channel-inspector.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/blob-storage-keys.png b/articles/media/bot-service-channel-webchat/blob-storage-keys.png deleted file mode 100644 index d3bdfc5e0..000000000 Binary files a/articles/media/bot-service-channel-webchat/blob-storage-keys.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/bot-service-channel-list.png b/articles/media/bot-service-channel-webchat/bot-service-channel-list.png deleted file mode 100644 index b2abdec4a..000000000 Binary files a/articles/media/bot-service-channel-webchat/bot-service-channel-list.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/bubbles-font-color.png b/articles/media/bot-service-channel-webchat/bubbles-font-color.png new file mode 100644 index 000000000..f956e400f Binary files /dev/null and b/articles/media/bot-service-channel-webchat/bubbles-font-color.png differ diff --git a/articles/media/bot-service-channel-webchat/chat-container-height-width.png b/articles/media/bot-service-channel-webchat/chat-container-height-width.png new file mode 100644 index 000000000..2bfb0d905 Binary files /dev/null and b/articles/media/bot-service-channel-webchat/chat-container-height-width.png differ diff --git a/articles/media/bot-service-channel-webchat/create-a-bot.png b/articles/media/bot-service-channel-webchat/create-a-bot.png deleted file mode 100644 index dfcf4f680..000000000 Binary files a/articles/media/bot-service-channel-webchat/create-a-bot.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/github-repo-attachments-custom.png b/articles/media/bot-service-channel-webchat/github-repo-attachments-custom.png new file mode 100644 index 000000000..6655f9f68 Binary files /dev/null and b/articles/media/bot-service-channel-webchat/github-repo-attachments-custom.png differ diff --git a/articles/media/bot-service-channel-webchat/image-attachment-custom.png b/articles/media/bot-service-channel-webchat/image-attachment-custom.png new file mode 100644 index 000000000..0cddcd8f8 Binary files /dev/null and b/articles/media/bot-service-channel-webchat/image-attachment-custom.png differ diff --git a/articles/media/bot-service-channel-webchat/secret-key.png b/articles/media/bot-service-channel-webchat/secret-key.png deleted file mode 100644 index 3ee2e664a..000000000 Binary files a/articles/media/bot-service-channel-webchat/secret-key.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/set-avatar-custom.png b/articles/media/bot-service-channel-webchat/set-avatar-custom.png new file mode 100644 index 000000000..55499c9e9 Binary files /dev/null and b/articles/media/bot-service-channel-webchat/set-avatar-custom.png differ diff --git a/articles/media/bot-service-channel-webchat/set-avatar-initials.png b/articles/media/bot-service-channel-webchat/set-avatar-initials.png new file mode 100644 index 000000000..ba9ce2c87 Binary files /dev/null and b/articles/media/bot-service-channel-webchat/set-avatar-initials.png differ diff --git a/articles/media/bot-service-channel-webchat/webchat-control.PNG b/articles/media/bot-service-channel-webchat/webchat-control.PNG new file mode 100644 index 000000000..7a80c14fc Binary files /dev/null and b/articles/media/bot-service-channel-webchat/webchat-control.PNG differ diff --git a/articles/media/bot-service-channel-webchat/webchat-sample-speech.png b/articles/media/bot-service-channel-webchat/webchat-sample-speech.png deleted file mode 100644 index 200ba6535..000000000 Binary files a/articles/media/bot-service-channel-webchat/webchat-sample-speech.png and /dev/null differ diff --git a/articles/media/bot-service-channel-webchat/webchat-sample.png b/articles/media/bot-service-channel-webchat/webchat-sample.png deleted file mode 100644 index 4b273ee27..000000000 Binary files a/articles/media/bot-service-channel-webchat/webchat-sample.png and /dev/null differ diff --git a/articles/media/bot-service-concept-templates/sdk-bot-cs-templates.png b/articles/media/bot-service-concept-templates/sdk-bot-cs-templates.png deleted file mode 100644 index 9caa06751..000000000 Binary files a/articles/media/bot-service-concept-templates/sdk-bot-cs-templates.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/bot-debug-java-breakpoint-caught.png b/articles/media/bot-service-debug-bot/bot-debug-java-breakpoint-caught.png new file mode 100644 index 000000000..135bd3f02 Binary files /dev/null and b/articles/media/bot-service-debug-bot/bot-debug-java-breakpoint-caught.png differ diff --git a/articles/media/bot-service-debug-bot/bot-debug-java-breakpoints.png b/articles/media/bot-service-debug-bot/bot-debug-java-breakpoints.png new file mode 100644 index 000000000..880694b6c Binary files /dev/null and b/articles/media/bot-service-debug-bot/bot-debug-java-breakpoints.png differ diff --git a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughost.png b/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughost.png deleted file mode 100644 index fa0aa825b..000000000 Binary files a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughost.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughostlogging.png b/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughostlogging.png deleted file mode 100644 index 06fdfb57a..000000000 Binary files a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-debughostlogging.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-envconfig.png b/articles/media/bot-service-debug-bot/csharp-azureservice-debug-envconfig.png deleted file mode 100644 index 9a177673b..000000000 Binary files a/articles/media/bot-service-debug-bot/csharp-azureservice-debug-envconfig.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/csharp-breakpoint-set.png b/articles/media/bot-service-debug-bot/csharp-breakpoint-set.png new file mode 100644 index 000000000..a3bf3cdf6 Binary files /dev/null and b/articles/media/bot-service-debug-bot/csharp-breakpoint-set.png differ diff --git a/articles/media/bot-service-debug-bot/emulator_inspector.png b/articles/media/bot-service-debug-bot/emulator_inspector.png deleted file mode 100644 index a1d253702..000000000 Binary files a/articles/media/bot-service-debug-bot/emulator_inspector.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/mac-azureservice-debug-emulator.png b/articles/media/bot-service-debug-bot/mac-azureservice-debug-emulator.png deleted file mode 100644 index 0625f5d09..000000000 Binary files a/articles/media/bot-service-debug-bot/mac-azureservice-debug-emulator.png and /dev/null differ diff --git a/articles/media/bot-service-debug-bot/mac-azureservice-emulator-config.png b/articles/media/bot-service-debug-bot/mac-azureservice-emulator-config.png deleted file mode 100644 index ef6b3b209..000000000 Binary files a/articles/media/bot-service-debug-bot/mac-azureservice-emulator-config.png and /dev/null differ diff --git a/articles/media/bot-service-debug-emulator/java_port_number.png b/articles/media/bot-service-debug-emulator/java_port_number.png new file mode 100644 index 000000000..53d06f365 Binary files /dev/null and b/articles/media/bot-service-debug-emulator/java_port_number.png differ diff --git a/articles/media/bot-service-debug-emulator/open_bot_emulator.png b/articles/media/bot-service-debug-emulator/open_bot_emulator.png index 71427782a..c0262970e 100644 Binary files a/articles/media/bot-service-debug-emulator/open_bot_emulator.png and b/articles/media/bot-service-debug-emulator/open_bot_emulator.png differ diff --git a/articles/media/bot-service-debug-emulator/python_port_number.png b/articles/media/bot-service-debug-emulator/python_port_number.png new file mode 100644 index 000000000..26433863c Binary files /dev/null and b/articles/media/bot-service-debug-emulator/python_port_number.png differ diff --git a/articles/media/bot-service-design-navigation/stubborn-bot.png b/articles/media/bot-service-design-navigation/stubborn-bot.png deleted file mode 100644 index 7d5176a83..000000000 Binary files a/articles/media/bot-service-design-navigation/stubborn-bot.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-embed-app/webchat-channel.png b/articles/media/bot-service-design-pattern-embed-app/webchat-channel.png deleted file mode 100644 index 0dff6a62e..000000000 Binary files a/articles/media/bot-service-design-pattern-embed-app/webchat-channel.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-integrate-browser/bot-to-web1.png b/articles/media/bot-service-design-pattern-integrate-browser/bot-to-web1.png deleted file mode 100644 index 5fbc4afca..000000000 Binary files a/articles/media/bot-service-design-pattern-integrate-browser/bot-to-web1.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/KnowledgeBaseConfig.PNG b/articles/media/bot-service-design-pattern-knowledge-base/KnowledgeBaseConfig.PNG deleted file mode 100644 index 150d418aa..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/KnowledgeBaseConfig.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/answerGenre.PNG b/articles/media/bot-service-design-pattern-knowledge-base/answerGenre.PNG deleted file mode 100644 index c01c1e87b..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/answerGenre.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/answerGenreOneWord.PNG b/articles/media/bot-service-design-pattern-knowledge-base/answerGenreOneWord.PNG deleted file mode 100644 index 5fa2672a8..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/answerGenreOneWord.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/devilMakesThreeScore.PNG b/articles/media/bot-service-design-pattern-knowledge-base/devilMakesThreeScore.PNG deleted file mode 100644 index ba31bcd09..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/devilMakesThreeScore.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/exampleQnAConvo.PNG b/articles/media/bot-service-design-pattern-knowledge-base/exampleQnAConvo.PNG deleted file mode 100644 index 7bba40363..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/exampleQnAConvo.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/facet.PNG b/articles/media/bot-service-design-pattern-knowledge-base/facet.PNG deleted file mode 100644 index 3b1fef89a..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/facet.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch.png b/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch.png deleted file mode 100644 index a35db0a9b..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch2.png b/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch2.png deleted file mode 100644 index b1b2c8908..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/fuzzySearch2.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo1.png b/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo1.png deleted file mode 100644 index 55c91f359..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo1.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo2.png b/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo2.png deleted file mode 100644 index 15def5d73..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo2.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo3.png b/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo3.png deleted file mode 100644 index c0fd371a4..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo3.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo4.png b/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo4.png deleted file mode 100644 index 83dce59dc..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/guidedConvo4.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/search3.PNG b/articles/media/bot-service-design-pattern-knowledge-base/search3.PNG deleted file mode 100644 index 0dc0790da..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/search3.PNG and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/searchScore1.png b/articles/media/bot-service-design-pattern-knowledge-base/searchScore1.png deleted file mode 100644 index 7170ac7ae..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/searchScore1.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/searchScore2.png b/articles/media/bot-service-design-pattern-knowledge-base/searchScore2.png deleted file mode 100644 index 84bbf444d..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/searchScore2.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-knowledge-base/training.png b/articles/media/bot-service-design-pattern-knowledge-base/training.png deleted file mode 100644 index 8b6133131..000000000 Binary files a/articles/media/bot-service-design-pattern-knowledge-base/training.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-task-automation/simple-task1.png b/articles/media/bot-service-design-pattern-task-automation/simple-task1.png deleted file mode 100644 index f061ebec8..000000000 Binary files a/articles/media/bot-service-design-pattern-task-automation/simple-task1.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-task-automation/simple-task2.png b/articles/media/bot-service-design-pattern-task-automation/simple-task2.png deleted file mode 100644 index e5ea80897..000000000 Binary files a/articles/media/bot-service-design-pattern-task-automation/simple-task2.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-task-automation/simple-task3.png b/articles/media/bot-service-design-pattern-task-automation/simple-task3.png deleted file mode 100644 index bb05fdbd3..000000000 Binary files a/articles/media/bot-service-design-pattern-task-automation/simple-task3.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-task-automation/simple-task4.png b/articles/media/bot-service-design-pattern-task-automation/simple-task4.png deleted file mode 100644 index 7a09a6d21..000000000 Binary files a/articles/media/bot-service-design-pattern-task-automation/simple-task4.png and /dev/null differ diff --git a/articles/media/bot-service-design-pattern-task-automation/simple-task5.png b/articles/media/bot-service-design-pattern-task-automation/simple-task5.png deleted file mode 100644 index 9886ba9d3..000000000 Binary files a/articles/media/bot-service-design-pattern-task-automation/simple-task5.png and /dev/null differ diff --git a/articles/media/bot-service-design-user-experience/buy-house.png b/articles/media/bot-service-design-user-experience/buy-house.png deleted file mode 100644 index 159eb7239..000000000 Binary files a/articles/media/bot-service-design-user-experience/buy-house.png and /dev/null differ diff --git a/articles/media/bot-service-manage-speech-priming/speech-priming.png b/articles/media/bot-service-manage-speech-priming/speech-priming.png deleted file mode 100644 index c0dc22ad7..000000000 Binary files a/articles/media/bot-service-manage-speech-priming/speech-priming.png and /dev/null differ diff --git a/articles/media/bot-service-migrate-bot/channel-registration-bot.png b/articles/media/bot-service-migrate-bot/channel-registration-bot.png deleted file mode 100644 index d106a938d..000000000 Binary files a/articles/media/bot-service-migrate-bot/channel-registration-bot.png and /dev/null differ diff --git a/articles/media/bot-service-migrate-bot/functions-bot.png b/articles/media/bot-service-migrate-bot/functions-bot.png deleted file mode 100644 index 806a590a9..000000000 Binary files a/articles/media/bot-service-migrate-bot/functions-bot.png and /dev/null differ diff --git a/articles/media/bot-service-migrate-bot/web-app-bot.png b/articles/media/bot-service-migrate-bot/web-app-bot.png deleted file mode 100644 index f3c909858..000000000 Binary files a/articles/media/bot-service-migrate-bot/web-app-bot.png and /dev/null differ diff --git a/articles/media/bot-service-portal-configure-settings/bot-service-profile.png b/articles/media/bot-service-portal-configure-settings/bot-service-profile.png new file mode 100644 index 000000000..ea57b5c1d Binary files /dev/null and b/articles/media/bot-service-portal-configure-settings/bot-service-profile.png differ diff --git a/articles/media/bot-service-portal-configure-settings/bot-service-settings.PNG b/articles/media/bot-service-portal-configure-settings/bot-service-settings.PNG new file mode 100644 index 000000000..b9eb3d87f Binary files /dev/null and b/articles/media/bot-service-portal-configure-settings/bot-service-settings.PNG differ diff --git a/articles/media/bot-service-portal-configure-settings/bot-settings-blade.png b/articles/media/bot-service-portal-configure-settings/bot-settings-blade.png deleted file mode 100644 index 4855c7371..000000000 Binary files a/articles/media/bot-service-portal-configure-settings/bot-settings-blade.png and /dev/null differ diff --git a/articles/media/bot-service-troubleshoot/kudu-cmd-console-bot-channel-dns.png b/articles/media/bot-service-troubleshoot/kudu-cmd-console-bot-channel-dns.png new file mode 100644 index 000000000..e9c1a9b98 Binary files /dev/null and b/articles/media/bot-service-troubleshoot/kudu-cmd-console-bot-channel-dns.png differ diff --git a/articles/media/bot-service-troubleshoot/kudu-cmd-console-channel-bot-dns.png b/articles/media/bot-service-troubleshoot/kudu-cmd-console-channel-bot-dns.png new file mode 100644 index 000000000..02bd925f6 Binary files /dev/null and b/articles/media/bot-service-troubleshoot/kudu-cmd-console-channel-bot-dns.png differ diff --git a/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-301.png b/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-301.png new file mode 100644 index 000000000..1f6cb0f7b Binary files /dev/null and b/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-301.png differ diff --git a/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-405.png b/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-405.png new file mode 100644 index 000000000..5f42451a7 Binary files /dev/null and b/articles/media/bot-service-troubleshoot/kudu-cmd-console-http-405.png differ diff --git a/articles/media/bot-service-troubleshoot/kudu-cmd-console.png b/articles/media/bot-service-troubleshoot/kudu-cmd-console.png new file mode 100644 index 000000000..13920845e Binary files /dev/null and b/articles/media/bot-service-troubleshoot/kudu-cmd-console.png differ diff --git a/articles/media/botbuilder-templates/cli-bot-download.png b/articles/media/botbuilder-templates/cli-bot-download.png deleted file mode 100644 index cab2046b7..000000000 Binary files a/articles/media/botbuilder-templates/cli-bot-download.png and /dev/null differ diff --git a/articles/media/botbuilder-templates/ludown-options.png b/articles/media/botbuilder-templates/ludown-options.png deleted file mode 100644 index c53332428..000000000 Binary files a/articles/media/botbuilder-templates/ludown-options.png and /dev/null differ diff --git a/articles/media/botbuilder-templates/msbot-cli-window.png b/articles/media/botbuilder-templates/msbot-cli-window.png deleted file mode 100644 index 550f1318a..000000000 Binary files a/articles/media/botbuilder-templates/msbot-cli-window.png and /dev/null differ diff --git a/articles/media/botbuilder-templates/qnamaker-init.png b/articles/media/botbuilder-templates/qnamaker-init.png deleted file mode 100644 index c7dbbe8e4..000000000 Binary files a/articles/media/botbuilder-templates/qnamaker-init.png and /dev/null differ diff --git a/articles/media/channels/GM-StepApp.png b/articles/media/channels/GM-StepApp.png deleted file mode 100644 index 9dd77ac92..000000000 Binary files a/articles/media/channels/GM-StepApp.png and /dev/null differ diff --git a/articles/media/channels/GM-StepClientIDToken.png b/articles/media/channels/GM-StepClientIDToken.png deleted file mode 100644 index d5a2070e0..000000000 Binary files a/articles/media/channels/GM-StepClientIDToken.png and /dev/null differ diff --git a/articles/media/channels/GM-StepClientId.png b/articles/media/channels/GM-StepClientId.png deleted file mode 100644 index d7dbe95ed..000000000 Binary files a/articles/media/channels/GM-StepClientId.png and /dev/null differ diff --git a/articles/media/channels/GM-StepPasteClientId.png b/articles/media/channels/GM-StepPasteClientId.png deleted file mode 100644 index 87940d823..000000000 Binary files a/articles/media/channels/GM-StepPasteClientId.png and /dev/null differ diff --git a/articles/media/channels/LINE-channel-setting-1.png b/articles/media/channels/LINE-channel-setting-1.png deleted file mode 100644 index 3381bcfab..000000000 Binary files a/articles/media/channels/LINE-channel-setting-1.png and /dev/null differ diff --git a/articles/media/channels/LINE-channel-setting-2.png b/articles/media/channels/LINE-channel-setting-2.png deleted file mode 100644 index 38006f362..000000000 Binary files a/articles/media/channels/LINE-channel-setting-2.png and /dev/null differ diff --git a/articles/media/channels/LINE-channel-type-selection.png b/articles/media/channels/LINE-channel-type-selection.png deleted file mode 100644 index ae8b80da3..000000000 Binary files a/articles/media/channels/LINE-channel-type-selection.png and /dev/null differ diff --git a/articles/media/channels/LINE-create-channel.png b/articles/media/channels/LINE-create-channel.png deleted file mode 100644 index a41d8636c..000000000 Binary files a/articles/media/channels/LINE-create-channel.png and /dev/null differ diff --git a/articles/media/channels/LINE-detailed-settings.png b/articles/media/channels/LINE-detailed-settings.png new file mode 100644 index 000000000..ab6f2eb5d Binary files /dev/null and b/articles/media/channels/LINE-detailed-settings.png differ diff --git a/articles/media/channels/LINE-screenshot-1.png b/articles/media/channels/LINE-screenshot-1.png deleted file mode 100644 index bbe263961..000000000 Binary files a/articles/media/channels/LINE-screenshot-1.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-10.png b/articles/media/channels/LINE-screenshot-10.png deleted file mode 100644 index 412d1a301..000000000 Binary files a/articles/media/channels/LINE-screenshot-10.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-11.png b/articles/media/channels/LINE-screenshot-11.png deleted file mode 100644 index e8a3da515..000000000 Binary files a/articles/media/channels/LINE-screenshot-11.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-12.jpg b/articles/media/channels/LINE-screenshot-12.jpg deleted file mode 100644 index 172ba9d42..000000000 Binary files a/articles/media/channels/LINE-screenshot-12.jpg and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-2.png b/articles/media/channels/LINE-screenshot-2.png deleted file mode 100644 index 85519f182..000000000 Binary files a/articles/media/channels/LINE-screenshot-2.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-4.png b/articles/media/channels/LINE-screenshot-4.png deleted file mode 100644 index ee3d3a785..000000000 Binary files a/articles/media/channels/LINE-screenshot-4.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-5.png b/articles/media/channels/LINE-screenshot-5.png deleted file mode 100644 index 5800903e5..000000000 Binary files a/articles/media/channels/LINE-screenshot-5.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-6.png b/articles/media/channels/LINE-screenshot-6.png deleted file mode 100644 index 3396b7851..000000000 Binary files a/articles/media/channels/LINE-screenshot-6.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-7.png b/articles/media/channels/LINE-screenshot-7.png deleted file mode 100644 index c7dea64fd..000000000 Binary files a/articles/media/channels/LINE-screenshot-7.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-8.png b/articles/media/channels/LINE-screenshot-8.png deleted file mode 100644 index 0d8e87dbe..000000000 Binary files a/articles/media/channels/LINE-screenshot-8.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-9.png b/articles/media/channels/LINE-screenshot-9.png deleted file mode 100644 index 44f2bdced..000000000 Binary files a/articles/media/channels/LINE-screenshot-9.png and /dev/null differ diff --git a/articles/media/channels/LINE-screenshot-conversation.jpg b/articles/media/channels/LINE-screenshot-conversation.jpg deleted file mode 100644 index e6bcfc7a8..000000000 Binary files a/articles/media/channels/LINE-screenshot-conversation.jpg and /dev/null differ diff --git a/articles/media/channels/LINE-webhook-settings.png b/articles/media/channels/LINE-webhook-settings.png new file mode 100644 index 000000000..01abb6339 Binary files /dev/null and b/articles/media/channels/LINE-webhook-settings.png differ diff --git a/articles/media/channels/alexa-create-skill-options.png b/articles/media/channels/alexa-create-skill-options.png new file mode 100644 index 000000000..162241cf4 Binary files /dev/null and b/articles/media/channels/alexa-create-skill-options.png differ diff --git a/articles/media/channels/alexa-create-skill-options2.png b/articles/media/channels/alexa-create-skill-options2.png new file mode 100644 index 000000000..b697d1040 Binary files /dev/null and b/articles/media/channels/alexa-create-skill-options2.png differ diff --git a/articles/media/channels/bing-category.png b/articles/media/channels/bing-category.png deleted file mode 100644 index ff4e39b7a..000000000 Binary files a/articles/media/channels/bing-category.png and /dev/null differ diff --git a/articles/media/channels/bing-contosoResult.png b/articles/media/channels/bing-contosoResult.png deleted file mode 100644 index d64dd5b58..000000000 Binary files a/articles/media/channels/bing-contosoResult.png and /dev/null differ diff --git a/articles/media/channels/bing-contosoWeb.png b/articles/media/channels/bing-contosoWeb.png deleted file mode 100644 index d856f55f3..000000000 Binary files a/articles/media/channels/bing-contosoWeb.png and /dev/null differ diff --git a/articles/media/channels/bing-general.png b/articles/media/channels/bing-general.png deleted file mode 100644 index 44e473fad..000000000 Binary files a/articles/media/channels/bing-general.png and /dev/null differ diff --git a/articles/media/channels/bing-language.png b/articles/media/channels/bing-language.png deleted file mode 100644 index cbb041dd8..000000000 Binary files a/articles/media/channels/bing-language.png and /dev/null differ diff --git a/articles/media/channels/bing-privacy.png b/articles/media/channels/bing-privacy.png deleted file mode 100644 index 317e81224..000000000 Binary files a/articles/media/channels/bing-privacy.png and /dev/null differ diff --git a/articles/media/channels/bing-publisher.png b/articles/media/channels/bing-publisher.png deleted file mode 100644 index 9660a0747..000000000 Binary files a/articles/media/channels/bing-publisher.png and /dev/null differ diff --git a/articles/media/channels/bing-toggle.png b/articles/media/channels/bing-toggle.png deleted file mode 100644 index 351f0a398..000000000 Binary files a/articles/media/channels/bing-toggle.png and /dev/null differ diff --git a/articles/media/channels/bing_business_bot.png b/articles/media/channels/bing_business_bot.png deleted file mode 100644 index f3e54db94..000000000 Binary files a/articles/media/channels/bing_business_bot.png and /dev/null differ diff --git a/articles/media/channels/connect-to-bing.png b/articles/media/channels/connect-to-bing.png deleted file mode 100644 index fe2a01391..000000000 Binary files a/articles/media/channels/connect-to-bing.png and /dev/null differ diff --git a/articles/media/channels/connect-to-channels.png b/articles/media/channels/connect-to-channels.png deleted file mode 100644 index 31f682ac8..000000000 Binary files a/articles/media/channels/connect-to-channels.png and /dev/null differ diff --git a/articles/media/channels/cortana-AddUserProfile.png b/articles/media/channels/cortana-AddUserProfile.png deleted file mode 100644 index f7e4f9265..000000000 Binary files a/articles/media/channels/cortana-AddUserProfile.png and /dev/null differ diff --git a/articles/media/channels/cortana-addchannel.png b/articles/media/channels/cortana-addchannel.png deleted file mode 100644 index 33dff537a..000000000 Binary files a/articles/media/channels/cortana-addchannel.png and /dev/null differ diff --git a/articles/media/channels/cortana-connectedAccount.png b/articles/media/channels/cortana-connectedAccount.png deleted file mode 100644 index b0a74102f..000000000 Binary files a/articles/media/channels/cortana-connectedAccount.png and /dev/null differ diff --git a/articles/media/channels/cortana-defaultsettings.png b/articles/media/channels/cortana-defaultsettings.png deleted file mode 100644 index 857ed1913..000000000 Binary files a/articles/media/channels/cortana-defaultsettings.png and /dev/null differ diff --git a/articles/media/channels/cortana-manage-knowledge-store.png b/articles/media/channels/cortana-manage-knowledge-store.png deleted file mode 100644 index 06a21dfd5..000000000 Binary files a/articles/media/channels/cortana-manage-knowledge-store.png and /dev/null differ diff --git a/articles/media/channels/cortana-manageidentity-1.png b/articles/media/channels/cortana-manageidentity-1.png deleted file mode 100644 index cc5fcbc9b..000000000 Binary files a/articles/media/channels/cortana-manageidentity-1.png and /dev/null differ diff --git a/articles/media/channels/cortana-manageidentity-2.png b/articles/media/channels/cortana-manageidentity-2.png deleted file mode 100644 index 546821dd3..000000000 Binary files a/articles/media/channels/cortana-manageidentity-2.png and /dev/null differ diff --git a/articles/media/channels/cortana_generalInfo.png b/articles/media/channels/cortana_generalInfo.png deleted file mode 100644 index af0bb0e30..000000000 Binary files a/articles/media/channels/cortana_generalInfo.png and /dev/null differ diff --git a/articles/media/channels/cortana_skills-slide-02.png b/articles/media/channels/cortana_skills-slide-02.png deleted file mode 100644 index 7c53463e0..000000000 Binary files a/articles/media/channels/cortana_skills-slide-02.png and /dev/null differ diff --git a/articles/media/channels/cortana_skills-slide-03.png b/articles/media/channels/cortana_skills-slide-03.png deleted file mode 100644 index 980034c2f..000000000 Binary files a/articles/media/channels/cortana_skills-slide-03.png and /dev/null differ diff --git a/articles/media/channels/cortana_skills-slide-04.png b/articles/media/channels/cortana_skills-slide-04.png deleted file mode 100644 index 2afc19d34..000000000 Binary files a/articles/media/channels/cortana_skills-slide-04.png and /dev/null differ diff --git a/articles/media/channels/cortana_skills-slide-05.png b/articles/media/channels/cortana_skills-slide-05.png deleted file mode 100644 index 9dcdc70fc..000000000 Binary files a/articles/media/channels/cortana_skills-slide-05.png and /dev/null differ diff --git a/articles/media/channels/cortana_skills-slide-06.png b/articles/media/channels/cortana_skills-slide-06.png deleted file mode 100644 index a397aa666..000000000 Binary files a/articles/media/channels/cortana_skills-slide-06.png and /dev/null differ diff --git a/articles/media/channels/create_new_botlet1.png b/articles/media/channels/create_new_botlet1.png deleted file mode 100644 index 85cf1f259..000000000 Binary files a/articles/media/channels/create_new_botlet1.png and /dev/null differ diff --git a/articles/media/channels/direct-line-extension-architecture.png b/articles/media/channels/direct-line-extension-architecture.png index b28640157..2d138c2bf 100644 Binary files a/articles/media/channels/direct-line-extension-architecture.png and b/articles/media/channels/direct-line-extension-architecture.png differ diff --git a/articles/media/channels/direct-line-extension-extension-keys-net-client.png b/articles/media/channels/direct-line-extension-extension-keys-net-client.png deleted file mode 100644 index 82c3399e1..000000000 Binary files a/articles/media/channels/direct-line-extension-extension-keys-net-client.png and /dev/null differ diff --git a/articles/media/channels/direct-line-extension-extension-keys.png b/articles/media/channels/direct-line-extension-extension-keys.png deleted file mode 100644 index 56f33c260..000000000 Binary files a/articles/media/channels/direct-line-extension-extension-keys.png and /dev/null differ diff --git a/articles/media/channels/direct-line-extension-vnet.png b/articles/media/channels/direct-line-extension-vnet.png deleted file mode 100644 index 257673876..000000000 Binary files a/articles/media/channels/direct-line-extension-vnet.png and /dev/null differ diff --git a/articles/media/channels/fb-create-messenger-bot-app-id.png b/articles/media/channels/fb-create-messenger-bot-app-id.png deleted file mode 100644 index 4495af6f6..000000000 Binary files a/articles/media/channels/fb-create-messenger-bot-app-id.png and /dev/null differ diff --git a/articles/media/channels/fb-create-messenger-bot-app.png b/articles/media/channels/fb-create-messenger-bot-app.png deleted file mode 100644 index bccc6df20..000000000 Binary files a/articles/media/channels/fb-create-messenger-bot-app.png and /dev/null differ diff --git a/articles/media/channels/fb-credentials2.png b/articles/media/channels/fb-credentials2.png deleted file mode 100644 index 0cb29a2eb..000000000 Binary files a/articles/media/channels/fb-credentials2.png and /dev/null differ diff --git a/articles/media/channels/fb-integration.png b/articles/media/channels/fb-integration.png deleted file mode 100644 index bcf9ff2d6..000000000 Binary files a/articles/media/channels/fb-integration.png and /dev/null differ diff --git a/articles/media/channels/fb-keys.png b/articles/media/channels/fb-keys.png deleted file mode 100644 index f3430acb5..000000000 Binary files a/articles/media/channels/fb-keys.png and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-api-settings.PNG b/articles/media/channels/fb-messenger-bot-api-settings.PNG deleted file mode 100644 index 05022ede7..000000000 Binary files a/articles/media/channels/fb-messenger-bot-api-settings.PNG and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-config-channel.PNG b/articles/media/channels/fb-messenger-bot-config-channel.PNG deleted file mode 100644 index 3ffd29625..000000000 Binary files a/articles/media/channels/fb-messenger-bot-config-channel.PNG and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-config-webhooks-page.PNG b/articles/media/channels/fb-messenger-bot-config-webhooks-page.PNG deleted file mode 100644 index e6e838533..000000000 Binary files a/articles/media/channels/fb-messenger-bot-config-webhooks-page.PNG and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-config-webhooks.png b/articles/media/channels/fb-messenger-bot-config-webhooks.png deleted file mode 100644 index c66b4a80e..000000000 Binary files a/articles/media/channels/fb-messenger-bot-config-webhooks.png and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-enable-messenger.PNG b/articles/media/channels/fb-messenger-bot-enable-messenger.PNG deleted file mode 100644 index de02ec9f6..000000000 Binary files a/articles/media/channels/fb-messenger-bot-enable-messenger.PNG and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-get-appid-secret.png b/articles/media/channels/fb-messenger-bot-get-appid-secret.png deleted file mode 100644 index 0d0aa1892..000000000 Binary files a/articles/media/channels/fb-messenger-bot-get-appid-secret.png and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-permissions.png b/articles/media/channels/fb-messenger-bot-permissions.png deleted file mode 100644 index 1b3eb40e2..000000000 Binary files a/articles/media/channels/fb-messenger-bot-permissions.png and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-select-messenger-page.PNG b/articles/media/channels/fb-messenger-bot-select-messenger-page.PNG deleted file mode 100644 index 926c29950..000000000 Binary files a/articles/media/channels/fb-messenger-bot-select-messenger-page.PNG and /dev/null differ diff --git a/articles/media/channels/fb-messenger-bot-webhooks.PNG b/articles/media/channels/fb-messenger-bot-webhooks.PNG deleted file mode 100644 index 68b9231a9..000000000 Binary files a/articles/media/channels/fb-messenger-bot-webhooks.PNG and /dev/null differ diff --git a/articles/media/channels/fb-page.png b/articles/media/channels/fb-page.png deleted file mode 100644 index d56725cf4..000000000 Binary files a/articles/media/channels/fb-page.png and /dev/null differ diff --git a/articles/media/channels/fb-version-upgrade.png b/articles/media/channels/fb-version-upgrade.png deleted file mode 100644 index 2c4ab0e59..000000000 Binary files a/articles/media/channels/fb-version-upgrade.png and /dev/null differ diff --git a/articles/media/channels/kik-configure.png b/articles/media/channels/kik-configure.png deleted file mode 100644 index 842894faf..000000000 Binary files a/articles/media/channels/kik-configure.png and /dev/null differ diff --git a/articles/media/channels/kik-creds.png b/articles/media/channels/kik-creds.png deleted file mode 100644 index 469a42213..000000000 Binary files a/articles/media/channels/kik-creds.png and /dev/null differ diff --git a/articles/media/channels/kik-dev-portal.png b/articles/media/channels/kik-dev-portal.png deleted file mode 100644 index eef5d21ed..000000000 Binary files a/articles/media/channels/kik-dev-portal.png and /dev/null differ diff --git a/articles/media/channels/kik-phone.png b/articles/media/channels/kik-phone.png deleted file mode 100644 index eebda4887..000000000 Binary files a/articles/media/channels/kik-phone.png and /dev/null differ diff --git a/articles/media/channels/kik-signup.png b/articles/media/channels/kik-signup.png deleted file mode 100644 index 7d29b1bee..000000000 Binary files a/articles/media/channels/kik-signup.png and /dev/null differ diff --git a/articles/media/channels/new_botlet_1of2.png b/articles/media/channels/new_botlet_1of2.png deleted file mode 100644 index ecc28b4f4..000000000 Binary files a/articles/media/channels/new_botlet_1of2.png and /dev/null differ diff --git a/articles/media/channels/new_botlet_2of2.png b/articles/media/channels/new_botlet_2of2.png deleted file mode 100644 index b288700c6..000000000 Binary files a/articles/media/channels/new_botlet_2of2.png and /dev/null differ diff --git a/articles/media/channels/new_organization.png b/articles/media/channels/new_organization.png deleted file mode 100644 index 62a527cc2..000000000 Binary files a/articles/media/channels/new_organization.png and /dev/null differ diff --git a/articles/media/channels/publish_menu_tab_2.png b/articles/media/channels/publish_menu_tab_2.png deleted file mode 100644 index 232055705..000000000 Binary files a/articles/media/channels/publish_menu_tab_2.png and /dev/null differ diff --git a/articles/media/channels/s4b-enable.png b/articles/media/channels/s4b-enable.png deleted file mode 100644 index dcb5367d8..000000000 Binary files a/articles/media/channels/s4b-enable.png and /dev/null differ diff --git a/articles/media/channels/schema-transform-version.png b/articles/media/channels/schema-transform-version.png new file mode 100644 index 000000000..911c61adb Binary files /dev/null and b/articles/media/channels/schema-transform-version.png differ diff --git a/articles/media/channels/search/configure-search.png b/articles/media/channels/search/configure-search.png new file mode 100644 index 000000000..e926ece93 Binary files /dev/null and b/articles/media/channels/search/configure-search.png differ diff --git a/articles/media/channels/skype-addchannel.png b/articles/media/channels/skype-addchannel.png deleted file mode 100644 index 34c6b76da..000000000 Binary files a/articles/media/channels/skype-addchannel.png and /dev/null differ diff --git a/articles/media/channels/skype_configure.PNG b/articles/media/channels/skype_configure.PNG deleted file mode 100644 index 7bcac3212..000000000 Binary files a/articles/media/channels/skype_configure.PNG and /dev/null differ diff --git a/articles/media/channels/slack-AppCredentials.png b/articles/media/channels/slack-AppCredentials.png deleted file mode 100644 index cc469e681..000000000 Binary files a/articles/media/channels/slack-AppCredentials.png and /dev/null differ diff --git a/articles/media/channels/slack-CreateApp-AddBotUser.png b/articles/media/channels/slack-CreateApp-AddBotUser.png deleted file mode 100644 index 24bf1cb91..000000000 Binary files a/articles/media/channels/slack-CreateApp-AddBotUser.png and /dev/null differ diff --git a/articles/media/channels/slack-CreateApp.png b/articles/media/channels/slack-CreateApp.png deleted file mode 100644 index bdd475505..000000000 Binary files a/articles/media/channels/slack-CreateApp.png and /dev/null differ diff --git a/articles/media/channels/slack-CreateBot.png b/articles/media/channels/slack-CreateBot.png deleted file mode 100644 index 6e4fc4a6f..000000000 Binary files a/articles/media/channels/slack-CreateBot.png and /dev/null differ diff --git a/articles/media/channels/slack-EnableEvents.png b/articles/media/channels/slack-EnableEvents.png deleted file mode 100644 index 6067b28f8..000000000 Binary files a/articles/media/channels/slack-EnableEvents.png and /dev/null differ diff --git a/articles/media/channels/slack-EnableMessages.png b/articles/media/channels/slack-EnableMessages.png deleted file mode 100644 index 84fb0ed07..000000000 Binary files a/articles/media/channels/slack-EnableMessages.png and /dev/null differ diff --git a/articles/media/channels/slack-MessageURL.png b/articles/media/channels/slack-MessageURL.png deleted file mode 100644 index 1d50122da..000000000 Binary files a/articles/media/channels/slack-MessageURL.png and /dev/null differ diff --git a/articles/media/channels/slack-NewApp.png b/articles/media/channels/slack-NewApp.png deleted file mode 100644 index e68479a4d..000000000 Binary files a/articles/media/channels/slack-NewApp.png and /dev/null differ diff --git a/articles/media/channels/slack-RedirectURL.png b/articles/media/channels/slack-RedirectURL.png deleted file mode 100644 index 043bcf2e0..000000000 Binary files a/articles/media/channels/slack-RedirectURL.png and /dev/null differ diff --git a/articles/media/channels/slack-StepAuth.png b/articles/media/channels/slack-StepAuth.png deleted file mode 100644 index 9ec29228e..000000000 Binary files a/articles/media/channels/slack-StepAuth.png and /dev/null differ diff --git a/articles/media/channels/slack-SubmitCredentials.png b/articles/media/channels/slack-SubmitCredentials.png deleted file mode 100644 index 265f970ed..000000000 Binary files a/articles/media/channels/slack-SubmitCredentials.png and /dev/null differ diff --git a/articles/media/channels/slack-SubscribeEvents-a.PNG b/articles/media/channels/slack-SubscribeEvents-a.PNG deleted file mode 100644 index 23dfdf930..000000000 Binary files a/articles/media/channels/slack-SubscribeEvents-a.PNG and /dev/null differ diff --git a/articles/media/channels/slack-SubscribeEvents-b.PNG b/articles/media/channels/slack-SubscribeEvents-b.PNG deleted file mode 100644 index ccdc16d57..000000000 Binary files a/articles/media/channels/slack-SubscribeEvents-b.PNG and /dev/null differ diff --git a/articles/media/channels/slack-SubscribeEvents-c.PNG b/articles/media/channels/slack-SubscribeEvents-c.PNG deleted file mode 100644 index 5d423218d..000000000 Binary files a/articles/media/channels/slack-SubscribeEvents-c.PNG and /dev/null differ diff --git a/articles/media/channels/tg-StepBotCreated.png b/articles/media/channels/tg-StepBotCreated.png deleted file mode 100644 index df830b0cd..000000000 Binary files a/articles/media/channels/tg-StepBotCreated.png and /dev/null differ diff --git a/articles/media/channels/tg-StepNameBot.png b/articles/media/channels/tg-StepNameBot.png deleted file mode 100644 index 9a6cce37d..000000000 Binary files a/articles/media/channels/tg-StepNameBot.png and /dev/null differ diff --git a/articles/media/channels/tg-StepNewBot.png b/articles/media/channels/tg-StepNewBot.png deleted file mode 100644 index be03ce4e1..000000000 Binary files a/articles/media/channels/tg-StepNewBot.png and /dev/null differ diff --git a/articles/media/channels/tg-StepUsername.png b/articles/media/channels/tg-StepUsername.png deleted file mode 100644 index 668817f5e..000000000 Binary files a/articles/media/channels/tg-StepUsername.png and /dev/null differ diff --git a/articles/media/channels/tg-StepVisitBotFather.png b/articles/media/channels/tg-StepVisitBotFather.png index 758b7e835..d932e373f 100644 Binary files a/articles/media/channels/tg-StepVisitBotFather.png and b/articles/media/channels/tg-StepVisitBotFather.png differ diff --git a/articles/media/channels/tg-accessToken-Azure.png b/articles/media/channels/tg-accessToken-Azure.png deleted file mode 100644 index cca77b016..000000000 Binary files a/articles/media/channels/tg-accessToken-Azure.png and /dev/null differ diff --git a/articles/media/channels/tg-botEnabled-Azure.png b/articles/media/channels/tg-botEnabled-Azure.png deleted file mode 100644 index 8a86340a5..000000000 Binary files a/articles/media/channels/tg-botEnabled-Azure.png and /dev/null differ diff --git a/articles/media/channels/tg-connectBot-Azure.png b/articles/media/channels/tg-connectBot-Azure.png deleted file mode 100644 index d5b1bdca9..000000000 Binary files a/articles/media/channels/tg-connectBot-Azure.png and /dev/null differ diff --git a/articles/media/channels/tree_create_new_botlet.png b/articles/media/channels/tree_create_new_botlet.png deleted file mode 100644 index 424181599..000000000 Binary files a/articles/media/channels/tree_create_new_botlet.png and /dev/null differ diff --git a/articles/media/channels/twi-StepAuth.png b/articles/media/channels/twi-StepAuth.png index 79efa7a12..29404d8ca 100644 Binary files a/articles/media/channels/twi-StepAuth.png and b/articles/media/channels/twi-StepAuth.png differ diff --git a/articles/media/channels/twi-StepPhone.png b/articles/media/channels/twi-StepPhone.png deleted file mode 100644 index 6fb3a87e5..000000000 Binary files a/articles/media/channels/twi-StepPhone.png and /dev/null differ diff --git a/articles/media/channels/twi-StepPhone2.png b/articles/media/channels/twi-StepPhone2.png deleted file mode 100644 index 66f9d6a8c..000000000 Binary files a/articles/media/channels/twi-StepPhone2.png and /dev/null differ diff --git a/articles/media/channels/twi-StepPhone3.PNG b/articles/media/channels/twi-StepPhone3.PNG deleted file mode 100644 index 9bb160a37..000000000 Binary files a/articles/media/channels/twi-StepPhone3.PNG and /dev/null differ diff --git a/articles/media/channels/twi-StepSubmit.png b/articles/media/channels/twi-StepSubmit.png index c2fc3ec3c..25a75cca7 100644 Binary files a/articles/media/channels/twi-StepSubmit.png and b/articles/media/channels/twi-StepSubmit.png differ diff --git a/articles/media/channels/twi-StepTwiml.png b/articles/media/channels/twi-StepTwiml.png deleted file mode 100644 index 910febca4..000000000 Binary files a/articles/media/channels/twi-StepTwiml.png and /dev/null differ diff --git a/articles/media/channels/wechat-change-language.png b/articles/media/channels/wechat-change-language.png deleted file mode 100644 index 3b2cb21c7..000000000 Binary files a/articles/media/channels/wechat-change-language.png and /dev/null differ diff --git a/articles/media/channels/wechat-chat.png b/articles/media/channels/wechat-chat.png deleted file mode 100644 index 05502f2b1..000000000 Binary files a/articles/media/channels/wechat-chat.png and /dev/null differ diff --git a/articles/media/channels/wechat-register-account.png b/articles/media/channels/wechat-register-account.png deleted file mode 100644 index aa459f5a0..000000000 Binary files a/articles/media/channels/wechat-register-account.png and /dev/null differ diff --git a/articles/media/channels/wechat-sandbox-account-2.png b/articles/media/channels/wechat-sandbox-account-2.png deleted file mode 100644 index 013aad9b7..000000000 Binary files a/articles/media/channels/wechat-sandbox-account-2.png and /dev/null differ diff --git a/articles/media/channels/wechat-sandbox-account.png b/articles/media/channels/wechat-sandbox-account.png deleted file mode 100644 index 7fd436873..000000000 Binary files a/articles/media/channels/wechat-sandbox-account.png and /dev/null differ diff --git a/articles/media/channels/wechat-serviceaccount-console.png b/articles/media/channels/wechat-serviceaccount-console.png deleted file mode 100644 index 7ac33954a..000000000 Binary files a/articles/media/channels/wechat-serviceaccount-console.png and /dev/null differ diff --git a/articles/media/channels/wechat-subscribe.png b/articles/media/channels/wechat-subscribe.png deleted file mode 100644 index 412768fe6..000000000 Binary files a/articles/media/channels/wechat-subscribe.png and /dev/null differ diff --git a/articles/media/chatwidget-channel.png b/articles/media/chatwidget-channel.png deleted file mode 100644 index b0231f5f7..000000000 Binary files a/articles/media/chatwidget-channel.png and /dev/null differ diff --git a/articles/media/chatwidget-client.png b/articles/media/chatwidget-client.png deleted file mode 100644 index 0f82b9161..000000000 Binary files a/articles/media/chatwidget-client.png and /dev/null differ diff --git a/articles/media/chatwidget-overview.png b/articles/media/chatwidget-overview.png deleted file mode 100644 index f1f55ad28..000000000 Binary files a/articles/media/chatwidget-overview.png and /dev/null differ diff --git a/articles/media/complex-conversation-flows.png b/articles/media/complex-conversation-flows.png deleted file mode 100644 index 877e54974..000000000 Binary files a/articles/media/complex-conversation-flows.png and /dev/null differ diff --git a/articles/media/connector-getstarted-bot-running-localhost.png b/articles/media/connector-getstarted-bot-running-localhost.png deleted file mode 100644 index 1525bc259..000000000 Binary files a/articles/media/connector-getstarted-bot-running-localhost.png and /dev/null differ diff --git a/articles/media/connector-getstarted-create-project.png b/articles/media/connector-getstarted-create-project.png deleted file mode 100644 index e0baba586..000000000 Binary files a/articles/media/connector-getstarted-create-project.png and /dev/null differ diff --git a/articles/media/connector-getstarted-start-bot-locally.png b/articles/media/connector-getstarted-start-bot-locally.png deleted file mode 100644 index c566cbf72..000000000 Binary files a/articles/media/connector-getstarted-start-bot-locally.png and /dev/null differ diff --git a/articles/media/connector/auth_bot_connector_to_bot.png b/articles/media/connector/auth_bot_connector_to_bot.png deleted file mode 100644 index 3ca0ad168..000000000 Binary files a/articles/media/connector/auth_bot_connector_to_bot.png and /dev/null differ diff --git a/articles/media/connector/auth_bot_framework_emulator_to_bot.png b/articles/media/connector/auth_bot_framework_emulator_to_bot.png deleted file mode 100644 index 555faf93d..000000000 Binary files a/articles/media/connector/auth_bot_framework_emulator_to_bot.png and /dev/null differ diff --git a/articles/media/connector/auth_bot_to_bot_connector.png b/articles/media/connector/auth_bot_to_bot_connector.png deleted file mode 100644 index 0d395e135..000000000 Binary files a/articles/media/connector/auth_bot_to_bot_connector.png and /dev/null differ diff --git a/articles/media/continuous-deployment-consumption-download.png b/articles/media/continuous-deployment-consumption-download.png deleted file mode 100644 index 4db1dfd0e..000000000 Binary files a/articles/media/continuous-deployment-consumption-download.png and /dev/null differ diff --git a/articles/media/continuous-integration-disconnect.png b/articles/media/continuous-integration-disconnect.png deleted file mode 100644 index ffc32e8d0..000000000 Binary files a/articles/media/continuous-integration-disconnect.png and /dev/null differ diff --git a/articles/media/continuous-integration-sourcecontrolsystem.png b/articles/media/continuous-integration-sourcecontrolsystem.png deleted file mode 100644 index a4673a911..000000000 Binary files a/articles/media/continuous-integration-sourcecontrolsystem.png and /dev/null differ diff --git a/articles/media/continuous-integration-sources.png b/articles/media/continuous-integration-sources.png deleted file mode 100644 index 696614768..000000000 Binary files a/articles/media/continuous-integration-sources.png and /dev/null differ diff --git a/articles/media/cortana-add.png b/articles/media/cortana-add.png deleted file mode 100644 index 22aec25fd..000000000 Binary files a/articles/media/cortana-add.png and /dev/null differ diff --git a/articles/media/cortana/add-user-profile-data.png b/articles/media/cortana/add-user-profile-data.png deleted file mode 100644 index b39c3b320..000000000 Binary files a/articles/media/cortana/add-user-profile-data.png and /dev/null differ diff --git a/articles/media/cortana/cortana-add.png b/articles/media/cortana/cortana-add.png deleted file mode 100644 index de8b09612..000000000 Binary files a/articles/media/cortana/cortana-add.png and /dev/null differ diff --git a/articles/media/cortana/cortana-edit.png b/articles/media/cortana/cortana-edit.png deleted file mode 100644 index e9b64f3a0..000000000 Binary files a/articles/media/cortana/cortana-edit.png and /dev/null differ diff --git a/articles/media/cortana/cortana-invocation-name-callout.png b/articles/media/cortana/cortana-invocation-name-callout.png deleted file mode 100644 index 026530366..000000000 Binary files a/articles/media/cortana/cortana-invocation-name-callout.png and /dev/null differ diff --git a/articles/media/cortana/cortana-notebook.png b/articles/media/cortana/cortana-notebook.png deleted file mode 100644 index f969c21dc..000000000 Binary files a/articles/media/cortana/cortana-notebook.png and /dev/null differ diff --git a/articles/media/cortana/cortana-register.png b/articles/media/cortana/cortana-register.png deleted file mode 100644 index 76562bd7a..000000000 Binary files a/articles/media/cortana/cortana-register.png and /dev/null differ diff --git a/articles/media/cortana/cortana-speech-luis-priming.png b/articles/media/cortana/cortana-speech-luis-priming.png deleted file mode 100644 index ce1390ca2..000000000 Binary files a/articles/media/cortana/cortana-speech-luis-priming.png and /dev/null differ diff --git a/articles/media/debug-devtunnel/breakpoint.png b/articles/media/debug-devtunnel/breakpoint.png new file mode 100644 index 000000000..d0c82bced Binary files /dev/null and b/articles/media/debug-devtunnel/breakpoint.png differ diff --git a/articles/media/debug-devtunnel/devtunnel-forwarding-url.png b/articles/media/debug-devtunnel/devtunnel-forwarding-url.png new file mode 100644 index 000000000..8f2f0e1d0 Binary files /dev/null and b/articles/media/debug-devtunnel/devtunnel-forwarding-url.png differ diff --git a/articles/media/debug-devtunnel/messaging-endpoint.png b/articles/media/debug-devtunnel/messaging-endpoint.png new file mode 100644 index 000000000..79af76722 Binary files /dev/null and b/articles/media/debug-devtunnel/messaging-endpoint.png differ diff --git a/articles/media/debug-vscode/builder-debug-step1.png b/articles/media/debug-vscode/builder-debug-step1.png deleted file mode 100644 index 8b8bc09d6..000000000 Binary files a/articles/media/debug-vscode/builder-debug-step1.png and /dev/null differ diff --git a/articles/media/debug-vscode/builder-debug-step2.png b/articles/media/debug-vscode/builder-debug-step2.png deleted file mode 100644 index 8a7c950a4..000000000 Binary files a/articles/media/debug-vscode/builder-debug-step2.png and /dev/null differ diff --git a/articles/media/debug-vscode/builder-debug-step3.png b/articles/media/debug-vscode/builder-debug-step3.png deleted file mode 100644 index ec5da5753..000000000 Binary files a/articles/media/debug-vscode/builder-debug-step3.png and /dev/null differ diff --git a/articles/media/debug-vscode/builder-debug-step4.png b/articles/media/debug-vscode/builder-debug-step4.png deleted file mode 100644 index e337e2f4b..000000000 Binary files a/articles/media/debug-vscode/builder-debug-step4.png and /dev/null differ diff --git a/articles/media/debug-vscode/builder-debug-step5.png b/articles/media/debug-vscode/builder-debug-step5.png deleted file mode 100644 index 038d1e97e..000000000 Binary files a/articles/media/debug-vscode/builder-debug-step5.png and /dev/null differ diff --git a/articles/media/deploy-bot-cli/cli-help.png b/articles/media/deploy-bot-cli/cli-help.png new file mode 100644 index 000000000..6a8f85934 Binary files /dev/null and b/articles/media/deploy-bot-cli/cli-help.png differ diff --git a/articles/media/deploy-bot-cli/select-all-zip.png b/articles/media/deploy-bot-cli/select-all-zip.png new file mode 100644 index 000000000..849e71f24 Binary files /dev/null and b/articles/media/deploy-bot-cli/select-all-zip.png differ diff --git a/articles/media/deploy-bot-visual-studio/net-app-service-create.png b/articles/media/deploy-bot-visual-studio/net-app-service-create.png deleted file mode 100644 index d5b4f4169..000000000 Binary files a/articles/media/deploy-bot-visual-studio/net-app-service-create.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/net-configuration.png b/articles/media/deploy-bot-visual-studio/net-configuration.png deleted file mode 100644 index 9b026e728..000000000 Binary files a/articles/media/deploy-bot-visual-studio/net-configuration.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/net-destination.png b/articles/media/deploy-bot-visual-studio/net-destination.png deleted file mode 100644 index edd96520e..000000000 Binary files a/articles/media/deploy-bot-visual-studio/net-destination.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/net-dialog.png b/articles/media/deploy-bot-visual-studio/net-dialog.png deleted file mode 100644 index dbcc073d6..000000000 Binary files a/articles/media/deploy-bot-visual-studio/net-dialog.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/net-publish.png b/articles/media/deploy-bot-visual-studio/net-publish.png deleted file mode 100644 index 36ee396a0..000000000 Binary files a/articles/media/deploy-bot-visual-studio/net-publish.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-app-service-create.png b/articles/media/deploy-bot-visual-studio/node-app-service-create.png deleted file mode 100644 index c182236da..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-app-service-create.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-app-service.png b/articles/media/deploy-bot-visual-studio/node-app-service.png deleted file mode 100644 index 76ee41898..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-app-service.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-configuration.png b/articles/media/deploy-bot-visual-studio/node-configuration.png deleted file mode 100644 index 6b45ae141..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-configuration.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-create-project.png b/articles/media/deploy-bot-visual-studio/node-create-project.png deleted file mode 100644 index 816f457a5..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-create-project.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-dialog.png b/articles/media/deploy-bot-visual-studio/node-dialog.png deleted file mode 100644 index dbcc073d6..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-dialog.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-publish-to-azure.png b/articles/media/deploy-bot-visual-studio/node-publish-to-azure.png deleted file mode 100644 index 8770421f7..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-publish-to-azure.png and /dev/null differ diff --git a/articles/media/deploy-bot-visual-studio/node-publish.png b/articles/media/deploy-bot-visual-studio/node-publish.png deleted file mode 100644 index d6baedd0c..000000000 Binary files a/articles/media/deploy-bot-visual-studio/node-publish.png and /dev/null differ diff --git a/articles/media/designing-bots/bot-dunno.png b/articles/media/designing-bots/bot-dunno.png deleted file mode 100644 index 363c15ac8..000000000 Binary files a/articles/media/designing-bots/bot-dunno.png and /dev/null differ diff --git a/articles/media/designing-bots/bot1.png b/articles/media/designing-bots/bot1.png deleted file mode 100644 index 68ce598ad..000000000 Binary files a/articles/media/designing-bots/bot1.png and /dev/null differ diff --git a/articles/media/designing-bots/capabilities/proactive1.png b/articles/media/designing-bots/capabilities/proactive1.png deleted file mode 100644 index ca9b6b488..000000000 Binary files a/articles/media/designing-bots/capabilities/proactive1.png and /dev/null differ diff --git a/articles/media/designing-bots/capabilities/trigger-actions.png b/articles/media/designing-bots/capabilities/trigger-actions.png deleted file mode 100644 index e063944b8..000000000 Binary files a/articles/media/designing-bots/capabilities/trigger-actions.png and /dev/null differ diff --git a/articles/media/designing-bots/core/Slide.png b/articles/media/designing-bots/core/Slide.png deleted file mode 100644 index eeba3bde3..000000000 Binary files a/articles/media/designing-bots/core/Slide.png and /dev/null differ diff --git a/articles/media/designing-bots/core/dialogs-screens-small.png b/articles/media/designing-bots/core/dialogs-screens-small.png deleted file mode 100644 index 66bc8fc35..000000000 Binary files a/articles/media/designing-bots/core/dialogs-screens-small.png and /dev/null differ diff --git a/articles/media/designing-bots/core/hello-bot.png b/articles/media/designing-bots/core/hello-bot.png deleted file mode 100644 index 1ad0d6db7..000000000 Binary files a/articles/media/designing-bots/core/hello-bot.png and /dev/null differ diff --git a/articles/media/designing-bots/core/speech-limit1.png b/articles/media/designing-bots/core/speech-limit1.png deleted file mode 100644 index 94bd23dc3..000000000 Binary files a/articles/media/designing-bots/core/speech-limit1.png and /dev/null differ diff --git a/articles/media/designing-bots/core/speech-limit2.png b/articles/media/designing-bots/core/speech-limit2.png deleted file mode 100644 index 14bacf8e1..000000000 Binary files a/articles/media/designing-bots/core/speech-limit2.png and /dev/null differ diff --git a/articles/media/designing-bots/core/speech-limit3.png b/articles/media/designing-bots/core/speech-limit3.png deleted file mode 100644 index 77ae41b06..000000000 Binary files a/articles/media/designing-bots/core/speech-limit3.png and /dev/null differ diff --git a/articles/media/designing-bots/core/speech-limit4.png b/articles/media/designing-bots/core/speech-limit4.png deleted file mode 100644 index e3d9309af..000000000 Binary files a/articles/media/designing-bots/core/speech-limit4.png and /dev/null differ diff --git a/articles/media/designing-bots/gotit-bot.png b/articles/media/designing-bots/gotit-bot.png deleted file mode 100644 index df2b5e070..000000000 Binary files a/articles/media/designing-bots/gotit-bot.png and /dev/null differ diff --git a/articles/media/designing-bots/happy-bot.png b/articles/media/designing-bots/happy-bot.png deleted file mode 100644 index e06d49434..000000000 Binary files a/articles/media/designing-bots/happy-bot.png and /dev/null differ diff --git a/articles/media/designing-bots/patterns/QnAMakerAlternatives.PNG b/articles/media/designing-bots/patterns/QnAMakerAlternatives.PNG deleted file mode 100644 index cec82ea62..000000000 Binary files a/articles/media/designing-bots/patterns/QnAMakerAlternatives.PNG and /dev/null differ diff --git a/articles/media/designing-bots/patterns/Unity3D.png b/articles/media/designing-bots/patterns/Unity3D.png deleted file mode 100644 index 89930becb..000000000 Binary files a/articles/media/designing-bots/patterns/Unity3D.png and /dev/null differ diff --git a/articles/media/designing-bots/patterns/back-channel.png b/articles/media/designing-bots/patterns/back-channel.png deleted file mode 100644 index 881483f56..000000000 Binary files a/articles/media/designing-bots/patterns/back-channel.png and /dev/null differ diff --git a/articles/media/designing-bots/patterns/bot-as-agent-2.png b/articles/media/designing-bots/patterns/bot-as-agent-2.png new file mode 100644 index 000000000..4f5549af8 Binary files /dev/null and b/articles/media/designing-bots/patterns/bot-as-agent-2.png differ diff --git a/articles/media/designing-bots/patterns/bot-as-proxy-2.png b/articles/media/designing-bots/patterns/bot-as-proxy-2.png new file mode 100644 index 000000000..7796692c3 Binary files /dev/null and b/articles/media/designing-bots/patterns/bot-as-proxy-2.png differ diff --git a/articles/media/designing-bots/patterns/bot-to-web1.png b/articles/media/designing-bots/patterns/bot-to-web1.png deleted file mode 100644 index 5fbc4afca..000000000 Binary files a/articles/media/designing-bots/patterns/bot-to-web1.png and /dev/null differ diff --git a/articles/media/designing-bots/patterns/devilMakesThree.PNG b/articles/media/designing-bots/patterns/devilMakesThree.PNG deleted file mode 100644 index 411219a8e..000000000 Binary files a/articles/media/designing-bots/patterns/devilMakesThree.PNG and /dev/null differ diff --git a/articles/media/designing-bots/sad-bot.png b/articles/media/designing-bots/sad-bot.png deleted file mode 100644 index 24fa9ec41..000000000 Binary files a/articles/media/designing-bots/sad-bot.png and /dev/null differ diff --git a/articles/media/designing-bots/thinking-bot.png b/articles/media/designing-bots/thinking-bot.png deleted file mode 100644 index 88013fa05..000000000 Binary files a/articles/media/designing-bots/thinking-bot.png and /dev/null differ diff --git a/articles/media/direct-line-configure.png b/articles/media/direct-line-configure.png index a2058b857..fdf1d3033 100644 Binary files a/articles/media/direct-line-configure.png and b/articles/media/direct-line-configure.png differ diff --git a/articles/media/directline/channels-registrated-resource.PNG b/articles/media/directline/channels-registrated-resource.PNG deleted file mode 100644 index 60f06c409..000000000 Binary files a/articles/media/directline/channels-registrated-resource.PNG and /dev/null differ diff --git a/articles/media/directline/channels-registration-config.PNG b/articles/media/directline/channels-registration-config.PNG deleted file mode 100644 index c2fe8ea2b..000000000 Binary files a/articles/media/directline/channels-registration-config.PNG and /dev/null differ diff --git a/articles/media/directline/channels-registration.PNG b/articles/media/directline/channels-registration.PNG deleted file mode 100644 index c462a15e1..000000000 Binary files a/articles/media/directline/channels-registration.PNG and /dev/null differ diff --git a/articles/media/directline/channels-resource-group-deployments.PNG b/articles/media/directline/channels-resource-group-deployments.PNG deleted file mode 100644 index edbf3eb93..000000000 Binary files a/articles/media/directline/channels-resource-group-deployments.PNG and /dev/null differ diff --git a/articles/media/directline/channels-resource-group-inputs.PNG b/articles/media/directline/channels-resource-group-inputs.PNG deleted file mode 100644 index 2e21a941c..000000000 Binary files a/articles/media/directline/channels-resource-group-inputs.PNG and /dev/null differ diff --git a/articles/media/directory.jpg b/articles/media/directory.jpg deleted file mode 100644 index 098846fb6..000000000 Binary files a/articles/media/directory.jpg and /dev/null differ diff --git a/articles/media/dotnet-core-authentication/create-asp-net-core-2x-project.png b/articles/media/dotnet-core-authentication/create-asp-net-core-2x-project.png deleted file mode 100644 index 5e0bacc0e..000000000 Binary files a/articles/media/dotnet-core-authentication/create-asp-net-core-2x-project.png and /dev/null differ diff --git a/articles/media/dotnet-core-authentication/nuget-package-net-core-version.png b/articles/media/dotnet-core-authentication/nuget-package-net-core-version.png deleted file mode 100644 index 89af8a075..000000000 Binary files a/articles/media/dotnet-core-authentication/nuget-package-net-core-version.png and /dev/null differ diff --git a/articles/media/emulator-v4/bot-file-dropdown.png b/articles/media/emulator-v4/bot-file-dropdown.png deleted file mode 100644 index 7dc212599..000000000 Binary files a/articles/media/emulator-v4/bot-file-dropdown.png and /dev/null differ diff --git a/articles/media/emulator-v4/botfile-generated.png b/articles/media/emulator-v4/botfile-generated.png deleted file mode 100644 index 7c167303e..000000000 Binary files a/articles/media/emulator-v4/botfile-generated.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-azure-login.PNG b/articles/media/emulator-v4/emulator-azure-login.PNG index 97836c14c..a40c725a8 100644 Binary files a/articles/media/emulator-v4/emulator-azure-login.PNG and b/articles/media/emulator-v4/emulator-azure-login.PNG differ diff --git a/articles/media/emulator-v4/emulator-connect-luis-btn.png b/articles/media/emulator-v4/emulator-connect-luis-btn.png deleted file mode 100644 index 74f9d2b9f..000000000 Binary files a/articles/media/emulator-v4/emulator-connect-luis-btn.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-connect-qna-btn.png b/articles/media/emulator-v4/emulator-connect-qna-btn.png deleted file mode 100644 index fa531669b..000000000 Binary files a/articles/media/emulator-v4/emulator-connect-qna-btn.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-disable-data-1.png b/articles/media/emulator-v4/emulator-disable-data-1.png index ed692d05f..6f909ef5e 100644 Binary files a/articles/media/emulator-v4/emulator-disable-data-1.png and b/articles/media/emulator-v4/emulator-disable-data-1.png differ diff --git a/articles/media/emulator-v4/emulator-disable-data-2.png b/articles/media/emulator-v4/emulator-disable-data-2.png deleted file mode 100644 index f5e9b2e5a..000000000 Binary files a/articles/media/emulator-v4/emulator-disable-data-2.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-disable-data-3.png b/articles/media/emulator-v4/emulator-disable-data-3.png deleted file mode 100644 index bb17b5102..000000000 Binary files a/articles/media/emulator-v4/emulator-disable-data-3.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-endpoint.png b/articles/media/emulator-v4/emulator-endpoint.png deleted file mode 100644 index 233fc4fae..000000000 Binary files a/articles/media/emulator-v4/emulator-endpoint.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-live-chat.png b/articles/media/emulator-v4/emulator-live-chat.png deleted file mode 100644 index 344e51f8e..000000000 Binary files a/articles/media/emulator-v4/emulator-live-chat.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-load-transcript.png b/articles/media/emulator-v4/emulator-load-transcript.png deleted file mode 100644 index 3703eacd1..000000000 Binary files a/articles/media/emulator-v4/emulator-load-transcript.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-localhost-settings.png b/articles/media/emulator-v4/emulator-localhost-settings.png deleted file mode 100644 index 448f60fee..000000000 Binary files a/articles/media/emulator-v4/emulator-localhost-settings.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-ngrok-path.png b/articles/media/emulator-v4/emulator-ngrok-path.png deleted file mode 100644 index e86c20d2e..000000000 Binary files a/articles/media/emulator-v4/emulator-ngrok-path.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-open-bot.png b/articles/media/emulator-v4/emulator-open-bot.png deleted file mode 100644 index 4eab91310..000000000 Binary files a/articles/media/emulator-v4/emulator-open-bot.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-running-manage-state.png b/articles/media/emulator-v4/emulator-running-manage-state.png deleted file mode 100644 index aa4ba4d6e..000000000 Binary files a/articles/media/emulator-v4/emulator-running-manage-state.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-running.png b/articles/media/emulator-v4/emulator-running.png deleted file mode 100644 index a0e90a7a7..000000000 Binary files a/articles/media/emulator-v4/emulator-running.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-save-transcript.png b/articles/media/emulator-v4/emulator-save-transcript.png deleted file mode 100644 index af0b73502..000000000 Binary files a/articles/media/emulator-v4/emulator-save-transcript.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-services.png b/articles/media/emulator-v4/emulator-services.png deleted file mode 100644 index 80742bc00..000000000 Binary files a/articles/media/emulator-v4/emulator-services.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-view-message-activity-02.png b/articles/media/emulator-v4/emulator-view-message-activity-02.png deleted file mode 100644 index 4ded0fe30..000000000 Binary files a/articles/media/emulator-v4/emulator-view-message-activity-02.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-view-message-activity-03.png b/articles/media/emulator-v4/emulator-view-message-activity-03.png deleted file mode 100644 index 53559e6ac..000000000 Binary files a/articles/media/emulator-v4/emulator-view-message-activity-03.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-view-message-activity.png b/articles/media/emulator-v4/emulator-view-message-activity.png deleted file mode 100644 index 7665a2695..000000000 Binary files a/articles/media/emulator-v4/emulator-view-message-activity.png and /dev/null differ diff --git a/articles/media/emulator-v4/emulator-welcome.png b/articles/media/emulator-v4/emulator-welcome.png index 39324b79b..5a6af934c 100644 Binary files a/articles/media/emulator-v4/emulator-welcome.png and b/articles/media/emulator-v4/emulator-welcome.png differ diff --git a/articles/media/emulator-v4/js-quickstart.png b/articles/media/emulator-v4/js-quickstart.png deleted file mode 100644 index 3e63364f3..000000000 Binary files a/articles/media/emulator-v4/js-quickstart.png and /dev/null differ diff --git a/articles/media/emulator-v4/msbot-cli-window.png b/articles/media/emulator-v4/msbot-cli-window.png deleted file mode 100644 index 550f1318a..000000000 Binary files a/articles/media/emulator-v4/msbot-cli-window.png and /dev/null differ diff --git a/articles/media/emulator-v4/multi-turn-prompt-validation.png b/articles/media/emulator-v4/multi-turn-prompt-validation.png deleted file mode 100644 index 2b5211d43..000000000 Binary files a/articles/media/emulator-v4/multi-turn-prompt-validation.png and /dev/null differ diff --git a/articles/media/emulator-v4/nlp-luis-sample-testing.png b/articles/media/emulator-v4/nlp-luis-sample-testing.png deleted file mode 100644 index 9bbfdc626..000000000 Binary files a/articles/media/emulator-v4/nlp-luis-sample-testing.png and /dev/null differ diff --git a/articles/media/emulator-v4/test-dialog-prompt.png b/articles/media/emulator-v4/test-dialog-prompt.png deleted file mode 100644 index 2d066a306..000000000 Binary files a/articles/media/emulator-v4/test-dialog-prompt.png and /dev/null differ diff --git a/articles/media/emulator/EmulatorUI.png b/articles/media/emulator/EmulatorUI.png deleted file mode 100644 index 3308ddf50..000000000 Binary files a/articles/media/emulator/EmulatorUI.png and /dev/null differ diff --git a/articles/media/emulator/dashboard.png b/articles/media/emulator/dashboard.png deleted file mode 100644 index 9b2a5f0a5..000000000 Binary files a/articles/media/emulator/dashboard.png and /dev/null differ diff --git a/articles/media/emulator/emulator-configure.jpg b/articles/media/emulator/emulator-configure.jpg deleted file mode 100644 index 36619f611..000000000 Binary files a/articles/media/emulator/emulator-configure.jpg and /dev/null differ diff --git a/articles/media/emulator/emulator-configure.png b/articles/media/emulator/emulator-configure.png deleted file mode 100644 index 78a004393..000000000 Binary files a/articles/media/emulator/emulator-configure.png and /dev/null differ diff --git a/articles/media/emulator/emulator-configure_ngrok_path.png b/articles/media/emulator/emulator-configure_ngrok_path.png deleted file mode 100644 index b80c2461a..000000000 Binary files a/articles/media/emulator/emulator-configure_ngrok_path.png and /dev/null differ diff --git a/articles/media/emulator/emulator-connect_localhost_credentials-original.png b/articles/media/emulator/emulator-connect_localhost_credentials-original.png deleted file mode 100644 index dc9ade3c3..000000000 Binary files a/articles/media/emulator/emulator-connect_localhost_credentials-original.png and /dev/null differ diff --git a/articles/media/emulator/emulator-connect_localhost_credentials.png b/articles/media/emulator/emulator-connect_localhost_credentials.png deleted file mode 100644 index 5284f8374..000000000 Binary files a/articles/media/emulator/emulator-connect_localhost_credentials.png and /dev/null differ diff --git a/articles/media/emulator/emulator-helloworld.jpg b/articles/media/emulator/emulator-helloworld.jpg deleted file mode 100644 index 105b993a6..000000000 Binary files a/articles/media/emulator/emulator-helloworld.jpg and /dev/null differ diff --git a/articles/media/emulator/emulator-helloworld.png b/articles/media/emulator/emulator-helloworld.png deleted file mode 100644 index 7dd06ce14..000000000 Binary files a/articles/media/emulator/emulator-helloworld.png and /dev/null differ diff --git a/articles/media/emulator/emulator-json.jpg b/articles/media/emulator/emulator-json.jpg deleted file mode 100644 index 7e5e02113..000000000 Binary files a/articles/media/emulator/emulator-json.jpg and /dev/null differ diff --git a/articles/media/emulator/emulator-ngrok-config.png b/articles/media/emulator/emulator-ngrok-config.png deleted file mode 100644 index 9884ec616..000000000 Binary files a/articles/media/emulator/emulator-ngrok-config.png and /dev/null differ diff --git a/articles/media/emulator/emulator-response.jpg b/articles/media/emulator/emulator-response.jpg deleted file mode 100644 index c51e0c1ac..000000000 Binary files a/articles/media/emulator/emulator-response.jpg and /dev/null differ diff --git a/articles/media/emulator/emulator-system-activities.png b/articles/media/emulator/emulator-system-activities.png deleted file mode 100644 index b27335ee2..000000000 Binary files a/articles/media/emulator/emulator-system-activities.png and /dev/null differ diff --git a/articles/media/emulator/emulator-testbot-cloud-config.png b/articles/media/emulator/emulator-testbot-cloud-config.png deleted file mode 100644 index ba4cf08f8..000000000 Binary files a/articles/media/emulator/emulator-testbot-cloud-config.png and /dev/null differ diff --git a/articles/media/emulator/emulator-testbot-ngrok-monitoring.png b/articles/media/emulator/emulator-testbot-ngrok-monitoring.png deleted file mode 100644 index 48fa4707b..000000000 Binary files a/articles/media/emulator/emulator-testbot-ngrok-monitoring.png and /dev/null differ diff --git a/articles/media/emulator/emulator-ui-new.png b/articles/media/emulator/emulator-ui-new.png deleted file mode 100644 index 5cf7c96c2..000000000 Binary files a/articles/media/emulator/emulator-ui-new.png and /dev/null differ diff --git a/articles/media/emulator/emulator-url.jpg b/articles/media/emulator/emulator-url.jpg deleted file mode 100644 index 7c91b6129..000000000 Binary files a/articles/media/emulator/emulator-url.jpg and /dev/null differ diff --git a/articles/media/emulator/emulator-url.png b/articles/media/emulator/emulator-url.png deleted file mode 100644 index e9af0059e..000000000 Binary files a/articles/media/emulator/emulator-url.png and /dev/null differ diff --git a/articles/media/emulator/newemulator-ubuntu.png b/articles/media/emulator/newemulator-ubuntu.png deleted file mode 100644 index 3340a20f9..000000000 Binary files a/articles/media/emulator/newemulator-ubuntu.png and /dev/null differ diff --git a/articles/media/hero-card.png b/articles/media/hero-card.png deleted file mode 100644 index 39601bd63..000000000 Binary files a/articles/media/hero-card.png and /dev/null differ diff --git a/articles/media/how-it-works/architecture-resize.png b/articles/media/how-it-works/architecture-resize.png deleted file mode 100644 index e6c419be7..000000000 Binary files a/articles/media/how-it-works/architecture-resize.png and /dev/null differ diff --git a/articles/media/how-it-works/architecture.png b/articles/media/how-it-works/architecture.png deleted file mode 100644 index c0b314bf2..000000000 Binary files a/articles/media/how-it-works/architecture.png and /dev/null differ diff --git a/articles/media/how-it-works/overview.png b/articles/media/how-it-works/overview.png deleted file mode 100644 index b5dda59be..000000000 Binary files a/articles/media/how-it-works/overview.png and /dev/null differ diff --git a/articles/media/how-to-create-single-tenant-bot/app-service-managed-identity.png b/articles/media/how-to-create-single-tenant-bot/app-service-managed-identity.png new file mode 100644 index 000000000..c4690de4e Binary files /dev/null and b/articles/media/how-to-create-single-tenant-bot/app-service-managed-identity.png differ diff --git a/articles/media/index/azure_portal.png b/articles/media/index/azure_portal.png deleted file mode 100644 index 9b7e35cc7..000000000 Binary files a/articles/media/index/azure_portal.png and /dev/null differ diff --git a/articles/media/index/i_guidelines.png b/articles/media/index/i_guidelines.png deleted file mode 100644 index 84fa73bc2..000000000 Binary files a/articles/media/index/i_guidelines.png and /dev/null differ diff --git a/articles/media/index/i_overview.png b/articles/media/index/i_overview.png deleted file mode 100644 index b926e63bd..000000000 Binary files a/articles/media/index/i_overview.png and /dev/null differ diff --git a/articles/media/index/logo_bot.svg b/articles/media/index/logo_bot.svg deleted file mode 100644 index 3fd22c8b5..000000000 --- a/articles/media/index/logo_bot.svg +++ /dev/null @@ -1 +0,0 @@ -azure-bot-service-square-circle \ No newline at end of file diff --git a/articles/media/index/logo_csharp.svg b/articles/media/index/logo_csharp.svg deleted file mode 100644 index 8e4362033..000000000 --- a/articles/media/index/logo_csharp.svg +++ /dev/null @@ -1 +0,0 @@ -logo_Csharp \ No newline at end of file diff --git a/articles/media/index/logo_java.svg b/articles/media/index/logo_java.svg deleted file mode 100644 index 4d87bef86..000000000 --- a/articles/media/index/logo_java.svg +++ /dev/null @@ -1 +0,0 @@ -logo_java \ No newline at end of file diff --git a/articles/media/index/logo_js.svg b/articles/media/index/logo_js.svg deleted file mode 100644 index db5904c4b..000000000 --- a/articles/media/index/logo_js.svg +++ /dev/null @@ -1 +0,0 @@ -logo_js \ No newline at end of file diff --git a/articles/media/index/logo_nodejs.svg b/articles/media/index/logo_nodejs.svg deleted file mode 100644 index 2deab0dd1..000000000 --- a/articles/media/index/logo_nodejs.svg +++ /dev/null @@ -1 +0,0 @@ -logo_nodejs \ No newline at end of file diff --git a/articles/media/index/logo_python.svg b/articles/media/index/logo_python.svg deleted file mode 100644 index 7292d8814..000000000 --- a/articles/media/index/logo_python.svg +++ /dev/null @@ -1 +0,0 @@ -logo_python \ No newline at end of file diff --git a/articles/media/key-vault/access-policies.png b/articles/media/key-vault/access-policies.png new file mode 100644 index 000000000..f33b2e28a Binary files /dev/null and b/articles/media/key-vault/access-policies.png differ diff --git a/articles/media/key-vault/choose-permission-model.png b/articles/media/key-vault/choose-permission-model.png new file mode 100644 index 000000000..0ffb71e90 Binary files /dev/null and b/articles/media/key-vault/choose-permission-model.png differ diff --git a/articles/media/key-vault/customer-managed-encryption.png b/articles/media/key-vault/customer-managed-encryption.png new file mode 100644 index 000000000..bb7ad63c0 Binary files /dev/null and b/articles/media/key-vault/customer-managed-encryption.png differ diff --git a/articles/media/key-vault/encryption-settings.png b/articles/media/key-vault/encryption-settings.png new file mode 100644 index 000000000..f91a8bb4a Binary files /dev/null and b/articles/media/key-vault/encryption-settings.png differ diff --git a/articles/media/key-vault/firewall-exception.png b/articles/media/key-vault/firewall-exception.png new file mode 100644 index 000000000..90429f8eb Binary files /dev/null and b/articles/media/key-vault/firewall-exception.png differ diff --git a/articles/media/key-vault/key-vault-rbac.png b/articles/media/key-vault/key-vault-rbac.png new file mode 100644 index 000000000..e7684d920 Binary files /dev/null and b/articles/media/key-vault/key-vault-rbac.png differ diff --git a/articles/media/key-vault/register-resource-provider.png b/articles/media/key-vault/register-resource-provider.png new file mode 100644 index 000000000..52a7a64b7 Binary files /dev/null and b/articles/media/key-vault/register-resource-provider.png differ diff --git a/articles/media/kik-signup.png b/articles/media/kik-signup.png deleted file mode 100644 index 7d29b1bee..000000000 Binary files a/articles/media/kik-signup.png and /dev/null differ diff --git a/articles/media/locale-dir.png b/articles/media/locale-dir.png deleted file mode 100644 index 2786d5ccb..000000000 Binary files a/articles/media/locale-dir.png and /dev/null differ diff --git a/articles/media/locale-namespacing.png b/articles/media/locale-namespacing.png deleted file mode 100644 index 40be78594..000000000 Binary files a/articles/media/locale-namespacing.png and /dev/null differ diff --git a/articles/media/logo-ms-dark.png b/articles/media/logo-ms-dark.png deleted file mode 100644 index edde52a63..000000000 Binary files a/articles/media/logo-ms-dark.png and /dev/null differ diff --git a/articles/media/logos/logo-bot-sdk.svg b/articles/media/logos/logo-bot-sdk.svg new file mode 100644 index 000000000..beb6c57c0 --- /dev/null +++ b/articles/media/logos/logo-bot-sdk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/articles/media/logos/logo-composer.svg b/articles/media/logos/logo-composer.svg new file mode 100644 index 000000000..4be0602f2 --- /dev/null +++ b/articles/media/logos/logo-composer.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/articles/media/logos/logo-copilot-studio.svg b/articles/media/logos/logo-copilot-studio.svg new file mode 100644 index 000000000..a3a5047ba --- /dev/null +++ b/articles/media/logos/logo-copilot-studio.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/articles/media/logos/logo-csharp.svg b/articles/media/logos/logo-csharp.svg new file mode 100644 index 000000000..3596c2c0c --- /dev/null +++ b/articles/media/logos/logo-csharp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/articles/media/logos/logo-js.svg b/articles/media/logos/logo-js.svg new file mode 100644 index 000000000..5abec38bd --- /dev/null +++ b/articles/media/logos/logo-js.svg @@ -0,0 +1,4 @@ + + + + diff --git a/articles/media/messenger_locationdialog_1.png b/articles/media/messenger_locationdialog_1.png deleted file mode 100644 index 58e76a889..000000000 Binary files a/articles/media/messenger_locationdialog_1.png and /dev/null differ diff --git a/articles/media/microsoft-payment.png b/articles/media/microsoft-payment.png deleted file mode 100644 index 6fb532d94..000000000 Binary files a/articles/media/microsoft-payment.png and /dev/null differ diff --git a/articles/media/msa-password-update-devportal-dashboard.png b/articles/media/msa-password-update-devportal-dashboard.png deleted file mode 100644 index 81ee03b88..000000000 Binary files a/articles/media/msa-password-update-devportal-dashboard.png and /dev/null differ diff --git a/articles/media/msa-password-update-devportal-edit.png b/articles/media/msa-password-update-devportal-edit.png deleted file mode 100644 index 9d7100a5b..000000000 Binary files a/articles/media/msa-password-update-devportal-edit.png and /dev/null differ diff --git a/articles/media/msa-password-update-msa-createnew.png b/articles/media/msa-password-update-msa-createnew.png deleted file mode 100644 index 2a0db71e0..000000000 Binary files a/articles/media/msa-password-update-msa-createnew.png and /dev/null differ diff --git a/articles/media/msa-password-update-msa-pwdcreated-copy.png b/articles/media/msa-password-update-msa-pwdcreated-copy.png deleted file mode 100644 index 44b1816df..000000000 Binary files a/articles/media/msa-password-update-msa-pwdcreated-copy.png and /dev/null differ diff --git a/articles/media/msa-password-update-msa-pwdcreated.png b/articles/media/msa-password-update-msa-pwdcreated.png deleted file mode 100644 index 44b1816df..000000000 Binary files a/articles/media/msa-password-update-msa-pwdcreated.png and /dev/null differ diff --git a/articles/media/msa-password-update-msa-pwddelete.png b/articles/media/msa-password-update-msa-pwddelete.png deleted file mode 100644 index a2b8add86..000000000 Binary files a/articles/media/msa-password-update-msa-pwddelete.png and /dev/null differ diff --git a/articles/media/msa-password-update-portal.png b/articles/media/msa-password-update-portal.png deleted file mode 100644 index 43af72f8b..000000000 Binary files a/articles/media/msa-password-update-portal.png and /dev/null differ diff --git a/articles/media/no.png b/articles/media/no.png deleted file mode 100644 index 1aa084e6a..000000000 Binary files a/articles/media/no.png and /dev/null differ diff --git a/articles/media/payments-bot-buy.png b/articles/media/payments-bot-buy.png deleted file mode 100644 index 06f87ab96..000000000 Binary files a/articles/media/payments-bot-buy.png and /dev/null differ diff --git a/articles/media/portal-app-insights-analytics.png b/articles/media/portal-app-insights-analytics.png deleted file mode 100644 index b306dffb8..000000000 Binary files a/articles/media/portal-app-insights-analytics.png and /dev/null differ diff --git a/articles/media/portal-app-insights-appid-apikey.png b/articles/media/portal-app-insights-appid-apikey.png index 9438a727d..786f9e7a4 100644 Binary files a/articles/media/portal-app-insights-appid-apikey.png and b/articles/media/portal-app-insights-appid-apikey.png differ diff --git a/articles/media/portal-channels-list.png b/articles/media/portal-channels-list.png deleted file mode 100644 index 65b1ec3a3..000000000 Binary files a/articles/media/portal-channels-list.png and /dev/null differ diff --git a/articles/media/portal-configure-bot.png b/articles/media/portal-configure-bot.png deleted file mode 100644 index aa1ba08c8..000000000 Binary files a/articles/media/portal-configure-bot.png and /dev/null differ diff --git a/articles/media/publishing-your-bot-deployment-credentials.png b/articles/media/publishing-your-bot-deployment-credentials.png deleted file mode 100644 index 9bffed0e0..000000000 Binary files a/articles/media/publishing-your-bot-deployment-credentials.png and /dev/null differ diff --git a/articles/media/python/quickstart/bot-running-locally.png b/articles/media/python/quickstart/bot-running-locally.png index 9d483395f..a50ec3123 100644 Binary files a/articles/media/python/quickstart/bot-running-locally.png and b/articles/media/python/quickstart/bot-running-locally.png differ diff --git a/articles/media/python/quickstart/connect-and-start.gif b/articles/media/python/quickstart/connect-and-start.gif deleted file mode 100644 index 32bbe1609..000000000 Binary files a/articles/media/python/quickstart/connect-and-start.gif and /dev/null differ diff --git a/articles/media/python/quickstart/connect-and-start.png b/articles/media/python/quickstart/connect-and-start.png deleted file mode 100644 index d4dd2b6c0..000000000 Binary files a/articles/media/python/quickstart/connect-and-start.png and /dev/null differ diff --git a/articles/media/python/quickstart/open-bot.png b/articles/media/python/quickstart/open-bot.png deleted file mode 100644 index 1a078574c..000000000 Binary files a/articles/media/python/quickstart/open-bot.png and /dev/null differ diff --git a/articles/media/python/quickstart/set-name-description.png b/articles/media/python/quickstart/set-name-description.png index a2a9afbdf..410fccbbe 100644 Binary files a/articles/media/python/quickstart/set-name-description.png and b/articles/media/python/quickstart/set-name-description.png differ diff --git a/articles/media/quickstart/emulator-hello-echo.png b/articles/media/quickstart/emulator-hello-echo.png new file mode 100644 index 000000000..d97ffd2d4 Binary files /dev/null and b/articles/media/quickstart/emulator-hello-echo.png differ diff --git a/articles/media/quickstart/emulator-open-bot.png b/articles/media/quickstart/emulator-open-bot.png new file mode 100644 index 000000000..4c96c7344 Binary files /dev/null and b/articles/media/quickstart/emulator-open-bot.png differ diff --git a/articles/media/real-time-media-bot-portal-certificates.png b/articles/media/real-time-media-bot-portal-certificates.png deleted file mode 100644 index c165e7bc6..000000000 Binary files a/articles/media/real-time-media-bot-portal-certificates.png and /dev/null differ diff --git a/articles/media/real-time-media-bot-portal-service-creation.png b/articles/media/real-time-media-bot-portal-service-creation.png deleted file mode 100644 index ab396e8a9..000000000 Binary files a/articles/media/real-time-media-bot-portal-service-creation.png and /dev/null differ diff --git a/articles/media/real-time-media-bot-publish-advanced-settings.png b/articles/media/real-time-media-bot-publish-advanced-settings.png deleted file mode 100644 index a6bc967fe..000000000 Binary files a/articles/media/real-time-media-bot-publish-advanced-settings.png and /dev/null differ diff --git a/articles/media/real-time-media-bot-publish-settings.png b/articles/media/real-time-media-bot-publish-settings.png deleted file mode 100644 index a8b47e30f..000000000 Binary files a/articles/media/real-time-media-bot-publish-settings.png and /dev/null differ diff --git a/articles/media/real-time-media-bot-publish-signin.png b/articles/media/real-time-media-bot-publish-signin.png deleted file mode 100644 index 146fa6ba1..000000000 Binary files a/articles/media/real-time-media-bot-publish-signin.png and /dev/null differ diff --git a/articles/media/receipt-card.png b/articles/media/receipt-card.png deleted file mode 100644 index 25cb6a204..000000000 Binary files a/articles/media/receipt-card.png and /dev/null differ diff --git a/articles/media/salon_bot_example.png b/articles/media/salon_bot_example.png deleted file mode 100644 index aad020589..000000000 Binary files a/articles/media/salon_bot_example.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-application-bot.png b/articles/media/scenarios/bot-service-scenario-application-bot.png deleted file mode 100644 index 572bf1d05..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-application-bot.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-commerce-bot.png b/articles/media/scenarios/bot-service-scenario-commerce-bot.png deleted file mode 100644 index fad24a4c9..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-commerce-bot.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-cortana-skill.png b/articles/media/scenarios/bot-service-scenario-cortana-skill.png deleted file mode 100644 index b2693c791..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-cortana-skill.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-enterprise-bot.png b/articles/media/scenarios/bot-service-scenario-enterprise-bot.png deleted file mode 100644 index 67e8ead4a..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-enterprise-bot.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-informational-bot.png b/articles/media/scenarios/bot-service-scenario-informational-bot.png deleted file mode 100644 index 265c614ce..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-informational-bot.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-informational.png b/articles/media/scenarios/bot-service-scenario-informational.png deleted file mode 100644 index a1630ab1d..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-informational.png and /dev/null differ diff --git a/articles/media/scenarios/bot-service-scenario-iot-bot.png b/articles/media/scenarios/bot-service-scenario-iot-bot.png deleted file mode 100644 index 15b52927d..000000000 Binary files a/articles/media/scenarios/bot-service-scenario-iot-bot.png and /dev/null differ diff --git a/articles/media/skype_multiaddress_1.png b/articles/media/skype_multiaddress_1.png deleted file mode 100644 index 02c6a3309..000000000 Binary files a/articles/media/skype_multiaddress_1.png and /dev/null differ diff --git a/articles/media/skype_multiresult_1.png b/articles/media/skype_multiresult_1.png deleted file mode 100644 index 02c6a3309..000000000 Binary files a/articles/media/skype_multiresult_1.png and /dev/null differ diff --git a/articles/media/skype_requiredaddress_1.png b/articles/media/skype_requiredaddress_1.png deleted file mode 100644 index f8f7a8374..000000000 Binary files a/articles/media/skype_requiredaddress_1.png and /dev/null differ diff --git a/articles/media/skype_singleaddress_1.png b/articles/media/skype_singleaddress_1.png deleted file mode 100644 index 5b3680e10..000000000 Binary files a/articles/media/skype_singleaddress_1.png and /dev/null differ diff --git a/articles/media/skype_singleaddress_2.png b/articles/media/skype_singleaddress_2.png deleted file mode 100644 index 06fca2558..000000000 Binary files a/articles/media/skype_singleaddress_2.png and /dev/null differ diff --git a/articles/media/teams/connect-teams-channel.png b/articles/media/teams/connect-teams-channel.png deleted file mode 100644 index fa52d8f35..000000000 Binary files a/articles/media/teams/connect-teams-channel.png and /dev/null differ diff --git a/articles/media/teams/get-embed-code.png b/articles/media/teams/get-embed-code.png deleted file mode 100644 index 019b772db..000000000 Binary files a/articles/media/teams/get-embed-code.png and /dev/null differ diff --git a/articles/media/teams/save-teams-channel.png b/articles/media/teams/save-teams-channel.png deleted file mode 100644 index 91bf2580f..000000000 Binary files a/articles/media/teams/save-teams-channel.png and /dev/null differ diff --git a/articles/media/troubleshooting-bot-framework-authentication_1.png b/articles/media/troubleshooting-bot-framework-authentication_1.png deleted file mode 100644 index d9bfc4313..000000000 Binary files a/articles/media/troubleshooting-bot-framework-authentication_1.png and /dev/null differ diff --git a/articles/media/troubleshooting-bot-framework-authentication_2.png b/articles/media/troubleshooting-bot-framework-authentication_2.png deleted file mode 100644 index 464a7f9e8..000000000 Binary files a/articles/media/troubleshooting-bot-framework-authentication_2.png and /dev/null differ diff --git a/articles/media/troubleshooting-bot-framework-authentication_3.png b/articles/media/troubleshooting-bot-framework-authentication_3.png deleted file mode 100644 index 057ad3eb2..000000000 Binary files a/articles/media/troubleshooting-bot-framework-authentication_3.png and /dev/null differ diff --git a/articles/media/upgrade/generate-new-password.png b/articles/media/upgrade/generate-new-password.png deleted file mode 100644 index 277c20272..000000000 Binary files a/articles/media/upgrade/generate-new-password.png and /dev/null differ diff --git a/articles/media/upgrade/manage-app-id.png b/articles/media/upgrade/manage-app-id.png deleted file mode 100644 index ecd0ea7fe..000000000 Binary files a/articles/media/upgrade/manage-app-id.png and /dev/null differ diff --git a/articles/media/upgrade/new-password-generated.png b/articles/media/upgrade/new-password-generated.png deleted file mode 100644 index e730b87ac..000000000 Binary files a/articles/media/upgrade/new-password-generated.png and /dev/null differ diff --git a/articles/media/video/satya-video.png b/articles/media/video/satya-video.png deleted file mode 100644 index 23a81066c..000000000 Binary files a/articles/media/video/satya-video.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-cognitivesericesaccount-selection.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-cognitivesericesaccount-selection.png deleted file mode 100644 index 5ec9e1408..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-cognitivesericesaccount-selection.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-configureappservice.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-configureappservice.png deleted file mode 100644 index de40c1f1c..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-configureappservice.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-connectspeechchannel.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-connectspeechchannel.png deleted file mode 100644 index b54e60a03..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-connectspeechchannel.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablestreamingsupport.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablestreamingsupport.png deleted file mode 100644 index f31f9d31a..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablestreamingsupport.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablewebsockets.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablewebsockets.png deleted file mode 100644 index 4c0635a82..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-enablewebsockets.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys.png deleted file mode 100644 index 41b57da4e..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys1.PNG b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys1.PNG deleted file mode 100644 index cb3399a6b..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-getspeechsecretkeys1.PNG and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-savechannel.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-savechannel.png deleted file mode 100644 index f932124ed..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-savechannel.png and /dev/null differ diff --git a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-selectchannel.png b/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-selectchannel.png deleted file mode 100644 index 46df65322..000000000 Binary files a/articles/media/voice-first-virtual-assistants/bot-service-channel-directlinespeech-selectchannel.png and /dev/null differ diff --git a/articles/media/what-is-bot-framework-components-placeholder.png b/articles/media/what-is-bot-framework-components-placeholder.png deleted file mode 100644 index e43765c2b..000000000 Binary files a/articles/media/what-is-bot-framework-components-placeholder.png and /dev/null differ diff --git a/articles/media/yes.png b/articles/media/yes.png deleted file mode 100644 index d2285c5c4..000000000 Binary files a/articles/media/yes.png and /dev/null differ diff --git a/articles/monitor-bot-service-reference.md b/articles/monitor-bot-service-reference.md new file mode 100644 index 000000000..f9b82ebd2 --- /dev/null +++ b/articles/monitor-bot-service-reference.md @@ -0,0 +1,84 @@ +--- +title: Monitoring data reference for Azure AI Bot Service +description: This article contains important reference material you need when you monitor Azure AI Bot Service. +ms.custom: + - horz-monitor + - evergreen +ms.topic: reference +author: iaanw +ms.author: iawilt +ms.service: azure-ai-bot-service +--- + +# Azure AI Bot Service monitoring data reference + +[!INCLUDE [horz-monitor-ref-intro](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-intro.md)] + +See [Monitor Azure AI Bot Service](monitor-bot-service.md) for details on the data you can collect for Azure AI Bot Service and how to use it. + +[!INCLUDE [horz-monitor-ref-metrics-intro](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-intro.md)] + +### Supported metrics for microsoft.botservice/botservices +The following table lists the metrics available for the microsoft.botservice/botservices resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [microsoft.botservice/botservices](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-botservices-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/botServices/channels +The following table lists the metrics available for the Microsoft.BotService/botServices/channels resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [Microsoft.BotService/botServices/channels](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-botservices-channels-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/botServices/connections +The following table lists the metrics available for the Microsoft.BotService/botServices/connections resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [Microsoft.BotService/botServices/connections](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-botservices-connections-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/checknameavailability +The following table lists the metrics available for the Microsoft.BotService/checknameavailability resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [Microsoft.BotService/checknameavailability](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-checknameavailability-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/hostsettings +The following table lists the metrics available for the Microsoft.BotService/hostsettings resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [microsoft.botservice/botservices](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-hostsettings-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/listauthserviceproviders +The following table lists the metrics available for the Microsoft.BotService/listauthserviceproviders resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [Microsoft.BotService/listauthserviceproviders](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-listauthserviceproviders-metrics-include.md)] + +### Supported metrics for Microsoft.BotService/listqnamakerendpointkeys +The following table lists the metrics available for the Microsoft.BotService/listqnamakerendpointkeys resource type. +[!INCLUDE [horz-monitor-ref-metrics-tableheader](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-tableheader.md)] +[!INCLUDE [Microsoft.BotService/listqnamakerendpointkeys](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/metrics/microsoft-botservice-listqnamakerendpointkeys-metrics-include.md)] + +[!INCLUDE [horz-monitor-ref-metrics-dimensions-intro](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-dimensions-intro.md)] +[!INCLUDE [horz-monitor-ref-metrics-dimensions](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-metrics-dimensions.md)] + +The **Dimensions** columns in the preceding metrics tables list the dimensions associated with Bot Service metrics. + +[!INCLUDE [horz-monitor-ref-resource-logs](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-resource-logs.md)] + +### Supported resource logs for microsoft.botservice/botservices +[!INCLUDE [Microsoft.BotService/botservices](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/reference/logs/microsoft-botservice-botservices-logs-include.md)] + +[!INCLUDE [horz-monitor-ref-logs-tables](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-logs-tables.md)] + +### Bot Services +Microsoft.BotService/botServices +- [AzureActivity](/azure/azure-monitor/reference/tables/azureactivity#columns) +- [ABSBotRequests](/azure/azure-monitor/reference/tables/absbotrequests#columns) + +[!INCLUDE [horz-monitor-ref-activity-log](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-activity-log.md)] + +- [Microsoft.BotService resource provider operations](/azure/role-based-access-control/resource-provider-operations#microsoftbotservice) + +[!INCLUDE [horz-monitor-ref-other-schemas](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-ref-other-schemas.md)] + +For a listing of the most common fields that bots log telemetry data into, see [Schema of bot analytics instrumentation](v4sdk/bot-builder-telemetry-analytics-queries.md#schema-of-bot-analytics-instrumentation). + +## Related content + +- See [Monitor Bot Service](monitor-bot-service.md) for a description of monitoring Bot Service. +- See [Monitor Azure resources with Azure Monitor](/azure/azure-monitor/essentials/monitor-azure-resource) for details on monitoring Azure resources. diff --git a/articles/monitor-bot-service.md b/articles/monitor-bot-service.md new file mode 100644 index 000000000..c1edbe905 --- /dev/null +++ b/articles/monitor-bot-service.md @@ -0,0 +1,166 @@ +--- +title: Monitor Azure AI Bot Service +description: Start here to learn how to monitor Azure AI Bot Service. +ms.custom: + - horz-monitor + - evergreen +ms.topic: how-to +author: iaanw +ms.author: iawilt +ms.service: azure-ai-bot-service +--- + +# Monitor Azure AI Bot Service + +[!INCLUDE [horz-monitor-intro](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-intro.md)] + +[!INCLUDE [horz-monitor-insights](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-insights.md)] + +Telemetry logging enables bot applications to send event data to telemetry services such as Application Insights. Telemetry offers insights into your bot by showing which features are used the most, detects unwanted behavior, and offers visibility into availability, performance, and usage. For more information about implementing telemetry in your bot using Application Insights, see [Add telemetry to your bot](v4sdk/bot-builder-telemetry.md). + +[!INCLUDE [horz-monitor-resource-types](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-resource-types.md)] +For more information about the resource types for Azure AI Bot Service, see [Bot Service monitoring data reference](monitor-bot-service-reference.md). + +[!INCLUDE [horz-monitor-data-storage](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-data-storage.md)] + + + +[!INCLUDE [horz-monitor-platform-metrics](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-platform-metrics.md)] +For a list of available metrics for Bot Service, see [Bot Service monitoring data reference](monitor-bot-service-reference.md#metrics). + +[!INCLUDE [horz-monitor-resource-logs](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-resource-logs.md)] +For the available resource log categories, their associated Log Analytics tables, and the logs schemas for Bot Service, see [Bot Service monitoring data reference](monitor-bot-service-reference.md#resource-logs). + +[!INCLUDE [horz-monitor-activity-log](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-activity-log.md)] + +[!INCLUDE [horz-monitor-analyze-data](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-analyze-data.md)] + +[!INCLUDE [horz-monitor-external-tools](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-external-tools.md)] + +[!INCLUDE [horz-monitor-kusto-queries](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-kusto-queries.md)] + +You can use the following queries to help monitor your bot resource. + +For more information and a collection of Kusto query examples for analyzing bot behavior, see [Analyze your bot's telemetry data](v4sdk/bot-builder-telemetry-analytics-queries.md). + +- Logs of Clients to Direct Line channel requests. + + ```kusto + ABSBotRequests + | where OperationName contains "ClientToDirectLine" + | sort by TimeGenerated desc + | limit 100 + ``` + +- Logs of requests from the Bot to channels. + + ```kusto + ABSBotRequests + | where OperationName contains "BotToChannel" + | sort by TimeGenerated desc + | limit 100 + ``` + +- Logs of requests from Channels to the bot. + + ```kusto + ABSBotRequests + | where OperationName contains "ChannelToBot" + | sort by TimeGenerated desc + | limit 100 + ``` + +- Logs of requests from Facebook to Azure AI Bot Service Facebook Channel. + + ```kusto + ABSBotRequests + | where OperationName contains "FacebookToChannel" + | sort by TimeGenerated desc + ``` + +- Logs of requests from Azure AI Bot Service Facebook Channel to Facebook API. + + ```kusto + ABSBotRequests + | where OperationName contains "ChannelToFacebookAPI" + | sort by TimeGenerated desc + ``` + +- Logs of requests to send activities from a client to Direct Line channel. + + ```kusto + ABSBotRequests + | where OperationName == 'SendAnActivity:ClientToDirectLine' + | sort by TimeGenerated desc + ``` + +- List of logs of unsuccessful requests. + + ```kusto + ABSBotRequests + | where ResultCode < 200 or ResultCode >= 300 + | sort by TimeGenerated desc + ``` + +- Line Chart showing Direct Line channel requests response codes. + + ```kusto + ABSBotRequests + | where Channel == "directline" + | summarize Number_Of_Requests = count() by tostring(ResultCode), bin(TimeGenerated, 5m) + | render timechart + ``` + +- Line Chart showing requests response times/duration per operation. + + ```kusto + ABSBotRequests + | summarize DurationMs = avg(DurationMs) by bin(TimeGenerated, 5m), OperationName + | render timechart + ``` + +- Line Chart showing requests response status codes. + + ```kusto + ABSBotRequests + | summarize Number_Of_Requests = count() by tostring(ResultCode), bin(TimeGenerated, 5m) + | render timechart + ``` + +- Pie Chart showing requests response status codes. + + ```kusto + ABSBotRequests + | summarize count() by tostring(ResultCode) + | render piechart + ``` + +- Pie Chart showing requests operations. + + ```kusto + ABSBotRequests + | summarize count() by tostring(OperationName) + | render piechart + ``` + +[!INCLUDE [horz-monitor-alerts](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-alerts.md)] + +[!INCLUDE [horz-monitor-insights-alerts](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-insights-alerts.md)] + +### Bot Service alert rules +The following table lists common and recommended alert rules for Bot Service. + +| Alert type | Condition | Description | +|:---|:---|:---| +| Metrics | Request Latency milliseconds exceed threshold | Signal source: Platform metrics | +| Metrics | Requests Traffic count is greater than threshold % | Signal source: Platform metrics | +| Activity log | A Bot Service is deleted | Signal source: Administrative | + +[!INCLUDE [horz-monitor-advisor-recommendations](~/../articles/reusable-content/ce-skilling/azure/includes/azure-monitor/horizontals/horz-monitor-advisor-recommendations.md)] + +## Related content + +- For more information about implementing telemetry in your bot using Application Insights, see [Add telemetry to your bot](v4sdk/bot-builder-telemetry.md). +- For a collection of Kusto query examples for analyzing bot behavior, see [Analyze your bot's telemetry data](v4sdk/bot-builder-telemetry-analytics-queries.md). +- For a reference of the metrics, logs, and other important values created for Bot Service, see [Bot Service monitoring data reference](monitor-bot-service-reference.md). +- For general details on monitoring Azure resources, see [Monitoring Azure resources with Azure Monitor](/azure/azure-monitor/essentials/monitor-azure-resource). diff --git a/articles/nodejs/TOC.md b/articles/nodejs/TOC.md deleted file mode 100644 index f018e4bb9..000000000 --- a/articles/nodejs/TOC.md +++ /dev/null @@ -1,33 +0,0 @@ -# [Bot Framework SDK for Node.js](bot-builder-nodejs-overview.md) -# [Key concepts](bot-builder-nodejs-concepts.md) -# Dialogs -## [Dialogs overview](bot-builder-nodejs-dialog-overview.md) -## [Define conversation steps](bot-builder-nodejs-dialog-waterfall.md) -## [Prompt for user input](bot-builder-nodejs-dialog-prompt.md) -## [Manage conversation flow](bot-builder-nodejs-dialog-manage-conversation-flow.md) -## [Replace dialogs](bot-builder-nodejs-dialog-replace.md) -## [Handle user actions](bot-builder-nodejs-dialog-actions.md) -# Messages -## [Create messages](bot-builder-nodejs-message-create.md) -## [Send and receive attachments](bot-builder-nodejs-send-receive-attachments.md) -## [Send proactive messages](bot-builder-nodejs-proactive-messages.md) -## [Add rich cards to messages](bot-builder-nodejs-send-rich-cards.md) -## [Add speech to messages](bot-builder-nodejs-text-to-speech.md) -## [Add input hints to messages](bot-builder-nodejs-send-input-hints.md) -## [Add suggested actions to messages](bot-builder-nodejs-send-suggested-actions.md) -## [Send a typing indicator](bot-builder-nodejs-send-typing-indicator.md) -## [Intercept messages](bot-builder-nodejs-intercept-messages.md) -# Channels -## [Build a Cortana skill](bot-builder-nodejs-cortana-skill.md) -## [Conduct Skype calls](bot-builder-nodejs-conduct-audio-calls.md) -# State Data -## [Manage state data](bot-builder-nodejs-state.md) -## [Manage state data with Cosmos DB](bot-builder-nodejs-state-azure-cosmosdb.md) -## [Manage state data with Table storage](bot-builder-nodejs-state-azure-table-storage.md) -# [Recognize intent from message content](bot-builder-nodejs-recognize-intent-messages.md) -# [Recognize intent with LUIS](bot-builder-nodejs-recognize-intent-luis.md) -# [Handle user and conversation events](bot-builder-nodejs-handle-conversation-events.md) -# [Support localization](bot-builder-nodejs-localization.md) -# [Use backchannel mechanism](bot-builder-nodejs-backchannel.md) -# [Request payment](bot-builder-nodejs-request-payment.md) -# [Add Azure Search](bot-builder-nodejs-search-azure.md) diff --git a/articles/nodejs/bot-builder-nodejs-backchannel.md b/articles/nodejs/bot-builder-nodejs-backchannel.md deleted file mode 100644 index 6e2467aea..000000000 --- a/articles/nodejs/bot-builder-nodejs-backchannel.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: Exchange information using the web control - Bot Service -description: Learn how to exchange information between the bot and a web page using the Bot Framework SDK for Node.js. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Use the backchannel mechanism - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -[!INCLUDE [Introduction to backchannel mechanism](../includes/snippet-backchannel.md)] - -## Walk through - -The open source web chat control accesses the Direct Line API by using a JavaScript class -called DirectLineJS. -The control can either create its own instance of Direct Line, or it can share one with the hosting page. -If the control shares an instance of Direct Line with the hosting page, -both the control and the page will be capable of sending and receiving activities. -The following diagram shows the high-level architecture of a website that supports bot functionality by -using the open source web (chat) control and the Direct Line API. - -![Backchannel](../media/designing-bots/patterns/back-channel.png) - -### Sample code - -In this example, the bot and web page will use the backchannel mechanism to exchange information that is invisible to the user. -The bot will request that the web page change its background color, and the -web page will notify the bot when the user clicks a button on the page. - -> [!NOTE] -> The code snippets in this article originate from -> the backchannel sample -> and the backchannel bot. - -#### Client-side code - -First, the web page creates a **DirectLine** object. - -```javascript -var botConnection = new BotChat.DirectLine(...); -``` - -Then, it shares the **DirectLine** object when creating the WebChat instance. - -```javascript -BotChat.App({ - botConnection: botConnection, - user: user, - bot: bot -}, document.getElementById("BotChatGoesHere")); -``` - -When the user clicks a button on the web page, the web page posts an activity of type "event" -to notify the bot that the button was clicked. - -```javascript -const postButtonMessage = () => { - botConnection - .postActivity({type: "event", value: "", from: {id: "me" }, name: "buttonClicked"}) - .subscribe(id => console.log("success")); - } -``` - -> [!TIP] -> Use the attributes `name` and `value` to communicate any information that the bot may need in order -> to properly interpret and/or respond to the event. - -Finally, the web page also listens for a specific event from the bot. -In this example, the web page listens for an activity where type="event" and name="changeBackground". -When it receives this type of activity, it changes the background color of the web page to the `value` specified by the activity. - -```javascript -botConnection.activity$ - .filter(activity => activity.type === "event" && activity.name === "changeBackground") - .subscribe(activity => changeBackgroundColor(activity.value)) -``` - -#### Server-side code - -The backchannel bot -creates an event by using a helper function. - -```javascript -var bot = new builder.UniversalBot(connector, - function (session) { - var reply = createEvent("changeBackground", session.message.text, session.message.address); - session.endDialog(reply); - } -); - -const createEvent = (eventName, value, address) => { - var msg = new builder.Message().address(address); - msg.data.type = "event"; - msg.data.name = eventName; - msg.data.value = value; - return msg; -} -``` - -Likewise, the bot also listens for events from the client. -In this example, if the bot receives an event with `name="buttonClicked"`, -it will send a message to the user to say "I see that you clicked a button." - -```javascript -bot.on("event", function (event) { - var msg = new builder.Message().address(event.address); - msg.data.textLocale = "en-us"; - if (event.name === "buttonClicked") { - msg.data.text = "I see that you clicked a button."; - } - bot.send(msg); -}) -``` - -## Additional resources - -- [Direct Line API][directLineAPI] -- Microsoft Bot Framework WebChat control -- Backchannel sample -- Backchannel bot - -[directLineAPI]: https://docs.botframework.com/restapi/directline3/#navtitle diff --git a/articles/nodejs/bot-builder-nodejs-concepts.md b/articles/nodejs/bot-builder-nodejs-concepts.md deleted file mode 100644 index d6ead5f87..000000000 --- a/articles/nodejs/bot-builder-nodejs-concepts.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Key concepts in the Bot Framework SDK for Node.js - Bot Service -description: Understand the key concepts and tools for building and deploying conversational bots available in the Bot Framework SDK for Node.js. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Key concepts in the Bot Framework SDK for Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-concepts.md) -> - [Node.js](../nodejs/bot-builder-nodejs-concepts.md) - -This article introduces key concepts in the Bot Framework SDK for Node.js. For an introduction to Bot Framework, see [Bot Framework overview](../overview-introduction-bot-framework.md). - -## Connector -The Bot Framework Connector is a service that connects your bot to multiple *channels*, which are clients like [Teams](https://docs.microsoft.com/microsoftteams/platform/concepts/bots/bots-create), Skype, Facebook, Slack, and SMS. - -The Connector facilitates communication between bot and user by relaying messages from bot to channel and from channel to bot. -Your bot's logic is hosted as a web service that receives messages from users through the Connector service, and your bot's replies are sent to the Connector using HTTPS POST. - -The Bot Framework SDK for Node.js provides the [UniversalBot][UniversalBot] and [ChatConnector][ChatConnector] classes for configuring the bot to send and receive messages through the Bot Framework Connector. The `UniversalBot` class forms the brains of your bot. It's responsible for managing all the conversations your bot has with a user. The `ChatConnector` class connects your bot to the Bot Framework Connector Service. -For an example that demonstrates using these classes, see [Create a bot with the Bot Framework SDK for Node.js](bot-builder-nodejs-quickstart.md). - -The Connector also normalizes the messages that the bot sends to channels so that you can develop your bot in a platform-agnostic way. Normalizing a message involves converting it from the Bot Framework’s schema into the channel’s schema. In cases where the channel does not support all aspects of the framework’s schema, the Connector will try to convert the message to a format that the channel supports. For example, if the bot sends a message that contains a card with action buttons to the SMS channel, the Connector may render the card as an image and include the actions as links in the message’s text. - -[!INCLUDE [Channel Inspector intro](~/includes/snippet-channel-inspector.md)] - -The `ChatConnector` requires an API endpoint to be setup within your bot. With the Node.js SDK, this is usually accomplished by installing the `restify` Node.js module. Bots can also be created for the console using the [ConsoleConnector][ConsoleConnector], which does not require an API endpoint. - -## Messages - -Messages can consist of text to be displayed, text to be spoken, attachments, rich cards, and suggested actions. You use the [session.send][SessionSend] method to send messages in response to a message from the user. Your bot may call `send` as many times as it likes in response to a message from the user. For an example that demonstrates this, see [Respond to user messages][RespondMessages]. - -For an example that demonstrates how to send a rich graphical card containing interactive buttons that the user clicks to initiate an action, see [Add rich cards to messages](bot-builder-nodejs-send-rich-cards.md). For an example that demonstrates how to send and receive attachments, see [Send attachments](bot-builder-nodejs-send-receive-attachments.md). For an example that demonstrates how to send a message that specifies text to be spoken by your bot on a speech-enabled channel, see [Add speech to messages](bot-builder-nodejs-text-to-speech.md). For an example that demonstrates how to send suggested actions, see [Send suggested actions](bot-builder-nodejs-send-suggested-actions.md). - -## Dialogs -Dialogs help you organize the conversational logic in your bot and are fundamental to [designing conversation flow](../bot-service-design-conversation-flow.md). For an introduction to dialogs, see [Manage a conversation with dialogs](bot-builder-nodejs-dialog-manage-conversation.md). - -## Actions -You'll want to design your bot to be able to handle interruptions like requests for cancellation or help at any time during the conversation flow. The Bot Framework SDK for Node.js provides global message handlers that trigger actions like cancellation or invoking other dialogs. - See [Handle user actions](bot-builder-nodejs-dialog-actions.md) for examples of how to use [triggerAction][triggerAction] handlers. - - - -## Recognizers -When users ask your bot for something, like "help" or "find news", your bot needs to understand what the user is asking for and then take the appropriate action. You can design your bot to recognize intents based on the user’s input and associate that intent with actions. - -You can use the built-in regular expression recognizer that the Bot Framework SDK provides, call an external service such as the LUIS API, or implement a custom recognizer to determine the user's intent. -See [Recognize user intent](bot-builder-nodejs-recognize-intent-messages.md) for examples that demonstrate how to add recognizers to your bot and use them to trigger actions. - - -## Saving State - -A key to good bot design is to track the context of a conversation, so that your bot remembers things like the last question the user asked. -Bots built using Bot Framework SDK are designed to be be stateless so that they can easily be scaled to run across multiple compute nodes. The Bot Framework provides a storage system that stores bot data, so that the bot web service can be scaled. Because of that you should generally avoid saving state using a global variable or function closure. Doing so will create issues when you want to scale out your bot. Instead, use the following properties of your bot's [session][Session] object to save data relative to a user or conversation: - -* **userData** stores information globally for the user across all conversations. -* **conversationData** stores information globally for a single conversation. This data is visible to everyone within the conversation so exercise with care when storing data to this property. It’s enabled by default and you can disable it using the bot's [persistConversationData][PersistConversationData] setting. -* **privateConversationData** stores information globally for a single conversation but it is private data specific to the current user. This data spans all dialogs so it’s useful for storing temporary state that you want cleaned up when the conversation ends. -* **dialogData** persists information for a single dialog instance. This is essential for storing temporary information in between the steps of a [waterfall](bot-builder-nodejs-dialog-waterfall.md) in a dialog. - -For examples that demonstrate how to use these properties to store and retrieve data, see [Manage state data](bot-builder-nodejs-state.md). - -## Natural language understanding - -Bot Builder lets you use LUIS to add natural language understanding to your bot using the [LuisRecognizer][LuisRecognizer] class. You can add an instance of a **LuisRecognizer** that references your published language model and then add handlers to take actions in response to the user's utterances. To see LUIS in action watch the following 10 minute tutorial: - -* [Microsoft LUIS Tutorial][LUISVideo] (video) - -## Next steps -> [!div class="nextstepaction"] -> [Dialogs overview](bot-builder-nodejs-dialog-overview.md) - - - -[PersistConversationData]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iuniversalbotsettings.html#persistconversationdata -[UniversalBot]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html -[ChatConnector]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html -[ConsoleConnector]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.consoleconnector.html - -[ChannelInspector]: ../bot-service-channels-reference.md - -[Session]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html -[SessionSend]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#send - -[triggerAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#triggeraction -[waterfall]: bot-builder-nodejs-prompts.md - -[RespondMessages]:bot-builder-nodejs-use-default-message-handler.md - -[LUISRecognizer]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.luisrecognizer -[LUISVideo]: https://vimeo.com/145499419 \ No newline at end of file diff --git a/articles/nodejs/bot-builder-nodejs-conduct-audio-calls.md b/articles/nodejs/bot-builder-nodejs-conduct-audio-calls.md deleted file mode 100644 index 94abd06ec..000000000 --- a/articles/nodejs/bot-builder-nodejs-conduct-audio-calls.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Conduct audio calls - Bot Service -description: Learn how to conduct audio calls with Skype in a bot using Node.js -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Support audio calls with Skype - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Skype supports a rich feature called Calling Bots. When enabled, users can place a voice call to your bot and interact with it using Interactive Voice Response (IVR). The Bot Builder for Node.js SDK includes a special [Calling SDK][calling_sdk] which developers can use to add calling features to their chat bot. - -The Calling SDK is very similar to the [Chat SDK][chat_sdk]. They have similar classes, share common constructs and you can even use the Chat SDK to send a message to the user you’re on a call with. The two SDKs are designed to run side-by-side but while they are similar, there are some significant differences and you should generally avoid mixing classes from the two libraries. - -## Create a calling bot -The following example code shows how the "Hello World" for a calling bot looks very similar to a regular chat bot. - -```javascript -var restify = require('restify'); -var calling = require('botbuilder-calling'); - -// Setup Restify Server -var server = restify.createServer(); -server.listen(process.env.port || process.env.PORT || 3978, function () { - console.log(`${server.name} listening to ${server.url}`); -}); - -// Create calling bot -var connector = new calling.CallConnector({ - callbackUrl: 'https:///api/calls', - appId: '', - appPassword: '' -}); -var bot = new calling.UniversalCallBot(connector); -server.post('/api/calls', connector.listen()); - -// Add root dialog -bot.dialog('/', function (session) { - session.send('Watson... come here!'); -}); -``` - -> [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -The emulator doesn’t currently support testing calling bots. To test your bot, you’ll need to go through most of the steps required to publish your bot. You will also need to use a Skype client to interact with the bot. - -### Enable the Skype channel -[Register your bot](../bot-service-quickstart-registration.md) and enable the Skype channel. You will need to provide a messaging endpoint when you register your bot. It is recommended that you pair your calling bot with a chat bot so the chat bot’s endpoint is what you would normally put in that field. If you’re only registering a calling bot you can simply paste your calling endpoint into that field. - -To enable the actual calling feature you’ll need to go into the Skype channel for your bot and turn on the calling feature. You’ll then be provided with a field to copy your calling endpoint into. Make sure you use the https ngrok link for the host portion of your calling endpoint. - -During the registration of your bot you’ll be assigned an app ID & password which you should paste into the connector settings for your hello world bot. You’ll also need to take your full calling link and paste that in for the callbackUrl setting. - -### Add bot to contacts -On your bot's registration page in the developer portal you’ll see an **add to Skype** button next to your bots Skype channel. Click the button to add your bot to your contact list in Skype. Once you do that you (and anyone you give the join link to) will be able to communicate with the bot. - -### Test your bot -You can test your bot using a Skype client. You should notice the call icon light up when you click on your bots contact entry (you may have to search for the bot to see it.) It can take a few minutes for the call icon to light up if you’ve added calling to an existing bot. - -If you press the call button it should dial your bot and you should hear it speak “Watson… come here!” and then hang up. - -## Calling basics -The [UniversalCallBot](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.universalcallbot) and [CallConnector](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callconnector) classes let you author a calling bot in much the same way you would a chat bot. You add dialogs to your bot that are essentially identical to [chat dialogs](bot-builder-nodejs-manage-conversation-flow.md). You can add [waterfalls](bot-builder-nodejs-prompts.md) to your bot. There is a session object, the [CallSession](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession) class, which contains added [answer()](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession#answer), [hangup()](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession#hangup), and [reject()](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession#reject) methods for managing the current call. In general, you don’t need to worry about these call control methods though as the CallSession has logic to automatically manage the call for you. The session will automatically answer the call if you take an action like sending a message or calling a built-in prompt. It will also automatically hangup/reject the call if you call [endConversation()](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession#endconversation) or it detects that you’ve stopped asking the caller questions (you didn’t call a built-in prompt.) - -Another difference between calling and chat bots is that while chat bots typically send messages, cards, and keyboards to a user a calling bot deals in Actions and Outcomes. Skype calling bots are required to create [workflows](http://docs.botframework.com/node/builder/calling-reference/interfaces/_botbuilder_d_.iworkflow) that are comprised of one or more [actions](http://docs.botframework.com/node/builder/calling-reference/interfaces/_botbuilder_d_.iaction). This is another thing that in practice you don’t have to worry too much about as the Bot Builder calling SDK will manage most of this for you. The [CallSession.send()](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.callsession#send) method lets you pass either actions or strings which it will turn into [PlayPromptActions](http://docs.botframework.com/node/builder/calling-reference/classes/_botbuilder_d_.playpromptaction). The session contains auto batching logic to combine multiple actions into a single workflow that’s submitted to the calling service so you can safely call send() multiple times. And you should rely on the SDK’s built-in [prompts](bot-builder-nodejs-prompts.md) to collect input from the user as they process all of the outcomes. - -[calling_sdk]: http://docs.botframework.com/node/builder/calling-reference/modules/_botbuilder_d_ -[chat_sdk]: http://docs.botframework.com/node/builder/chat-reference/modules/_botbuilder_d_ diff --git a/articles/nodejs/bot-builder-nodejs-cortana-skill.md b/articles/nodejs/bot-builder-nodejs-cortana-skill.md deleted file mode 100644 index a1612bb01..000000000 --- a/articles/nodejs/bot-builder-nodejs-cortana-skill.md +++ /dev/null @@ -1,449 +0,0 @@ ---- -title: Build a speech-enabled bot with Cortana skills - Bot Service -description: Learn how to build a speech-enabled bot with Cortana skills and the Bot Framework SDK for Node.js. -author: DeniseMak -manager: kamrani -ms.author: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/10/2019 -monikerRange: 'azure-bot-service-3.0' ---- -# Build a speech-enabled bot with Cortana skills - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-cortana-skill.md) -> - [Node.js](../nodejs/bot-builder-nodejs-cortana-skill.md) - -The Bot Framework SDK for Node.js enables you to a build speech-enabled bot by connecting it to the Cortana channel as a Cortana skill. -Cortana skills let you provide functionality through Cortana in response to spoken input from a user. - -> [!TIP] -> For more information on what a skill is, and what they can do, see [The Cortana Skills Kit][CortanaGetStarted]. - -Creating a Cortana skill using Bot Framework requires very little Cortana-specific knowledge and primarily consists of building a bot. One of the key differences from other bots that you may have created is that Cortana has both visual and audio components. For the visual component, Cortana provides an area of the canvas for rendering content such as cards. For the audio component, you provide text or SSML in your bot's messages, which Cortana reads to the user, giving your bot a voice. - -> [!NOTE] -> Cortana is available on many different devices. Some have a screen while others, like a standalone speaker, might not. You should make sure that your bot is capable of handling both scenarios. See [Cortana-specific entities][CortanaSpecificEntities] to learn how to check device information. - -## Adding speech to your bot - -Spoken messages from your bot are represented as Speech Synthesis Markup Language (SSML). The Bot Framework SDK lets you include SSML in your bot's responses to control what the bot says, in addition to what it shows. - -### session.say - -Your bot uses the **session.say** method to speak to the user, in place of **session.send**. It includes optional parameters for sending SSML output, as well as attachments like cards. - -The method has this format: - -```session.say(displayText: string, speechText: string, options?: object)``` - -| Parameter | Description | -|------|------| -| **displayText** | A textual message to display in Cortana's UI.| -| **speechText** | The text or SSML that Cortana reads to the user. | -| **options** | An [IMessage][IMessage] object that can contain an attachment or input hint. Input hints indicate whether the bot is accepting, expecting, or ignoring input. Card attachments are displayed in Cortana’s canvas below the **displayText** information. | - -The **inputHint** property helps indicate to Cortana whether your bot is expecting input. If you're using a built-in prompt, this value is automatically set to the default of **expectingInput**. - - -| Value | Description | -|------|------| -| **acceptingInput** | Your bot is passively ready for input but is not waiting on a response. Cortana accepts input from the user if the user holds down the microphone button.| -| **expectingInput** | Indicates that the bot is actively expecting a response from the user. Cortana listens for the user to speak into the microphone. | -||NOTE: Do _not_ use **expectingInput** on headless devices (devices without a display). See the [Cortana Skills Kit FAQ](https://review.docs.microsoft.com/cortana/skills/faq).| -| **ignoringInput** | Cortana is ignoring input. Your bot may send this hint if it is actively processing a request, and will ignore input from users until the request is complete. | - -The following example shows how Cortana reads plain text or SSML: - -```javascript - -// Have Cortana read plain text -session.say('This is the text that Cortana displays', 'This is the text that is spoken by Cortana.'); - -// Have Cortana read SSML -session.say('This is the text that Cortana displays', 'This is the text that is spoken by Cortana.'); - -``` - -This example shows how to let Cortana know that user input is expected. The microphone will be left open. - -```javascript - -// Add an InputHint to let Cortana know to expect user input -session.say('Hi there', 'Hi, what’s your name?', { - inputHint: builder.InputHint.expectingInput -}); - -``` - - -### Prompts - -In addition to using the **session.say()** method you can also pass text or SSML to built-in prompts using the **speak** and **retrySpeak** options. - -```javascript - -builder.Prompts.text(session, 'text based prompt', { - speak: 'Cortana reads this out initially', - retrySpeak: 'This message is repeated by Cortana after waiting a while for user input', - inputHint: builder.InputHint.expectingInput -}); - -``` - - - -To present the user with a list of choices, use **Prompts.choice**. The **synonyms** option allows for more flexibility in recognizing user utterances. The **value** option is returned in **results.response.entity**. The **action** option specifies the label that your bot displays for the choice. - -**Prompts.choice** supports ordinal choices. This means that the user can say "the first", "the second" or "the third" to choose an item in a list. For example, given the following prompt, if the user asked Cortana for "the second option", the prompt will return the value of 8. - -```javascript - - var choices = [ - { value: '4', action: { title: '4 Sides' }, synonyms: 'four|for|4 sided|4 sides' }, - { value: '8', action: { title: '8 Sides' }, synonyms: 'eight|ate|8 sided|8 sides' }, - { value: '12', action: { title: '12 Sides' }, synonyms: 'twelve|12 sided|12 sides' }, - { value: '20', action: { title: '20 Sides' }, synonyms: 'twenty|20 sided|20 sides' }, - ]; - builder.Prompts.choice(session, 'choose_sides', choices, { - speak: speak(session, 'choose_sides_ssml') // use helper function to format SSML - }); - -``` - -In the previous example, the SSML for the prompt's **speak** property is formatted by using strings stored in a localized prompts file with the following format. - -```json - -{ - "choose_sides": "__Number of Sides__", - "choose_sides_ssml": [ - "How many sides on the dice?", - "Pick your poison.", - "All the standard sizes are supported." - ] -} - -``` - -A helper function then builds the required root element of a Speech Synthesis Markup Language (SSML) document. - -```javascript - -module.exports.speak = function (template, params, options) { - options = options || {}; - var output = ''; - output += module.exports.vsprintf(template, params); - output += ''; - return output; -} -``` - -> [!TIP] -> You can find a small utility module (ssml.js) for building your bot's SSML-based responses in the [Roller sample skill](https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/demo-RollerSkill). -> There are also several useful SSML libraries available through [npm](https://www.npmjs.com/search?q=ssml) which make it easy to create well formatted SSML. - -## Display cards in Cortana - -In addition to spoken responses, Cortana can also display card attachments. Cortana supports the following rich cards: -* [HeroCard](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.herocard.html) -* [ReceiptCard](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.receiptcard.html) -* [ThumbnailCard](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.thumbnailcard.html) - -See [Card design best practices][CardDesign] to see what these cards look like inside Cortana. For an example of how to add a rich card to a bot, see [Send rich cards](bot-builder-nodejs-send-rich-cards.md). - -The following code demonstrates how to add the **speak** and **inputHint** properties to a message containing a Hero card. - -```javascript - -bot.dialog('HelpDialog', function (session) { - var card = new builder.HeroCard(session) - .title('help_title') - .buttons([ - builder.CardAction.imBack(session, 'roll some dice', 'Roll Dice'), - builder.CardAction.imBack(session, 'play yahtzee', 'Play Yahtzee') - ]); - var msg = new builder.Message(session) - .speak(speak(session, 'I\'m roller, the dice rolling bot. You can say \'roll some dice\'')) - .addAttachment(card) - .inputHint(builder.InputHint.acceptingInput); // Tell Cortana to accept input - session.send(msg).endDialog(); -}).triggerAction({ matches: /help/i }); - -/** This helper function builds the required root element of a Speech Synthesis Markup Language (SSML) document. */ -module.exports.speak = function (template, params, options) { - options = options || {}; - var output = ''; - output += module.exports.vsprintf(template, params); - output += ''; - return output; -} - -``` -## Sample: RollerSkill -The code in the following sections comes from a sample Cortana skill for rolling dice. Download the full code for the bot from the [BotBuilder-Samples repository](https://github.com/Microsoft/BotBuilder-Samples/tree/master/Node/demo-RollerSkill). - -You invoke the skill by saying its [invocation name][InvocationNameGuidelines] to Cortana. For the roller skill, after you [add the bot to the Cortana channel][CortanaChannel] and register it as a Cortana skill, you can invoke it by telling Cortana "Ask Roller" or "Ask Roller to roll dice". - -### Explore the code - -The RollerSkill sample starts by opening a card with some buttons to tell the user which options are available to them. - -```javascript - -/** - * Create your bot with a default message handler that receive messages from the user. - * - This function is be called anytime the user's utterance isn't - * recognized by the other handlers in the bot. - */ -var bot = new builder.UniversalBot(connector, function (session) { - // Just redirect to our 'HelpDialog'. - session.replaceDialog('HelpDialog'); -}); - -//... - -bot.dialog('HelpDialog', function (session) { - var card = new builder.HeroCard(session) - .title('help_title') - .buttons([ - builder.CardAction.imBack(session, 'roll some dice', 'Roll Dice'), - builder.CardAction.imBack(session, 'play craps', 'Play Craps') - ]); - var msg = new builder.Message(session) - .speak(speak(session, 'help_ssml')) - .addAttachment(card) - .inputHint(builder.InputHint.acceptingInput); - session.send(msg).endDialog(); -}).triggerAction({ matches: /help/i }); -``` - -### Prompt the user for input - -The following dialog sets up a custom game for the bot to play. It -asks the user how many sides they want the dice to have and then -how many should be rolled. Once it has built the game structure -it will pass it to a separate 'PlayGameDialog'. - -To start the dialog, the **triggerAction()** handler on this dialog allows a user to say -something like "I'd like to roll some dice". It uses a regular expression to match the user's input but you could just as easily use a [LUIS intent](./bot-builder-nodejs-recognize-intent-luis.md). - - -```javascript -bot.dialog('CreateGameDialog', [ - function (session) { - // Initialize game structure. - // - dialogData gives us temporary storage of this data in between - // turns with the user. - var game = session.dialogData.game = { - type: 'custom', - sides: null, - count: null, - turns: 0 - }; - - var choices = [ - { value: '4', action: { title: '4 Sides' }, synonyms: 'four|for|4 sided|4 sides' }, - { value: '6', action: { title: '6 Sides' }, synonyms: 'six|sex|6 sided|6 sides' }, - { value: '8', action: { title: '8 Sides' }, synonyms: 'eight|8 sided|8 sides' }, - { value: '10', action: { title: '10 Sides' }, synonyms: 'ten|10 sided|10 sides' }, - { value: '12', action: { title: '12 Sides' }, synonyms: 'twelve|12 sided|12 sides' }, - { value: '20', action: { title: '20 Sides' }, synonyms: 'twenty|20 sided|20 sides' }, - ]; - builder.Prompts.choice(session, 'choose_sides', choices, { - speak: speak(session, 'choose_sides_ssml') - }); - }, - function (session, results) { - // Store users input - // - The response comes back as a find result with index & entity value matched. - var game = session.dialogData.game; - game.sides = Number(results.response.entity); - - /** - * Ask for number of dice. - */ - var prompt = session.gettext('choose_count', game.sides); - builder.Prompts.number(session, prompt, { - speak: speak(session, 'choose_count_ssml'), - minValue: 1, - maxValue: 100, - integerOnly: true - }); - }, - function (session, results) { - // Store users input - // - The response is already a number. - var game = session.dialogData.game; - game.count = results.response; - - /** - * Play the game we just created. - * - * replaceDialog() ends the current dialog and start a new - * one in its place. We can pass arguments to dialogs so we'll pass the - * 'PlayGameDialog' the game we created. - */ - session.replaceDialog('PlayGameDialog', { game: game }); - } -]).triggerAction({ matches: [ - /(roll|role|throw|shoot).*(dice|die|dye|bones)/i, - /new game/i - ]}); - -``` - -### Render results - - This dialog is our main game loop. The bot stores the game structure in - **session.conversationData** so that should the user say "roll again" we - can just re-roll the same set of dice again. - -```javascript - -bot.dialog('PlayGameDialog', function (session, args) { - // Get current or new game structure. - var game = args.game || session.conversationData.game; - if (game) { - // Generate rolls - var total = 0; - var rolls = []; - for (var i = 0; i < game.count; i++) { - var roll = Math.floor(Math.random() * game.sides) + 1; - if (roll > game.sides) { - // Accounts for 1 in a million chance random() generated a 1.0 - roll = game.sides; - } - total += roll; - rolls.push(roll); - } - - // Format roll results - var results = ''; - var multiLine = rolls.length > 5; - for (var i = 0; i < rolls.length; i++) { - if (i > 0) { - results += ' . '; - } - results += rolls[i]; - } - - // Render results using a card - var card = new builder.HeroCard(session) - .subtitle(game.count > 1 ? 'card_subtitle_plural' : 'card_subtitle_singular', game) - .buttons([ - builder.CardAction.imBack(session, 'roll again', 'Roll Again'), - builder.CardAction.imBack(session, 'new game', 'New Game') - ]); - if (multiLine) { - //card.title('card_title').text('\n\n' + results + '\n\n'); - card.text(results); - } else { - card.title(results); - } - var msg = new builder.Message(session).addAttachment(card); - - // Determine bots reaction for speech purposes - var reaction = 'normal'; - var min = game.count; - var max = game.count * game.sides; - var score = total/max; - if (score == 1.0) { - reaction = 'best'; - } else if (score == 0) { - reaction = 'worst'; - } else if (score <= 0.3) { - reaction = 'bad'; - } else if (score >= 0.8) { - reaction = 'good'; - } - - // Check for special craps rolls - if (game.type == 'craps') { - switch (total) { - case 2: - case 3: - case 12: - reaction = 'craps_lose'; - break; - case 7: - reaction = 'craps_seven'; - break; - case 11: - reaction = 'craps_eleven'; - break; - default: - reaction = 'craps_retry'; - break; - } - } - - // Build up spoken response - var spoken = ''; - if (game.turn == 0) { - spoken += session.gettext('start_' + game.type + '_game_ssml') + ' '; - } - spoken += session.gettext(reaction + '_roll_reaction_ssml'); - msg.speak(ssml.speak(spoken)); - - // Increment number of turns and store game to roll again - game.turn++; - session.conversationData.game = game; - - /** - * Send card and bot's reaction to user. - */ - - msg.inputHint(builder.InputHint.acceptingInput); - session.send(msg).endDialog(); - } else { - // User started session with "roll again" so let's just send them to - // the 'CreateGameDialog' - session.replaceDialog('CreateGameDialog'); - } -}).triggerAction({ matches: /(roll|role|throw|shoot) again/i }); - -``` - -## Next steps -If you have a bot running locally or deployed in the cloud, you can invoke it from Cortana. See [Test a Cortana skill](../bot-service-debug-cortana-skill.md) for the steps required to try out your Cortana Skill. - - -## Additional resources -* [The Cortana Skills Kit][CortanaGetStarted] -* [Add speech to messages](bot-builder-nodejs-text-to-speech.md) -* [SSML Reference][SSMLRef] -* [Voice design best practices for Cortana][VoiceDesign] -* [Card design best practices for Cortana][CardDesign] -* [Cortana Dev Center][CortanaDevCenter] -* [Testing and debugging best practices for Cortana][Cortana-TestBestPractice] - - -[CortanaGetStarted]: /cortana/getstarted -[BFPortal]: https://dev.botframework.com/ - - -[SSMLRef]: https://aka.ms/cortana-ssml -[IMessage]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html -[Send]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#send -[CortanaDevCenter]: https://developer.microsoft.com/cortana - -[CortanaSpecificEntities]: https://aka.ms/cortana-channel-data -[CortanaAuth]: https://aka.ms/add-auth-cortana-skill - -[InvocationNameGuidelines]: https://aka.ms/cortana-invocation-guidelines -[VoiceDesign]: https://aka.ms/cortana-design-voice -[CardDesign]: https://aka.ms/cortana-design-card -[Cortana-Debug]: https://aka.ms/cortana-enable-debug -[Cortana-Publish]: https://aka.ms/cortana-publish - - -[CortanaChannel]: https://aka.ms/bot-cortana-channel -[Cortana-TestBestPractice]: https://aka.ms/cortana-test-best-practice diff --git a/articles/nodejs/bot-builder-nodejs-dialog-actions.md b/articles/nodejs/bot-builder-nodejs-dialog-actions.md deleted file mode 100644 index e3c9305fd..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-actions.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -title: Handle user actions - Bot Service -description: Learn how to handle user actions by enabling your bot to listen for and handle user input containing certain keywords using the Bot Framework SDK for Node.js. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Handle user actions - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-global-handlers.md) -> - [Node.js](../nodejs/bot-builder-nodejs-dialog-actions.md) - -Users commonly attempt to access certain functionality within a bot by using keywords like "help", "cancel", or "start over." -Users do this in the middle of a conversation, when the bot is expecting a different response. By implementing **actions**, you can design your bot to handle such requests more gracefully. The handlers will examine user input for the keywords that you specify, such as "help", "cancel", or "start over," and respond appropriately. - -![how users talk](../media/designing-bots/capabilities/trigger-actions.png) - -## Action types - -The action types you can attach to a dialog are listed in the table below. The link for each action name will take you to a section that provide more details about that action. - -| Action | Scope | Description | -|------|------| ---- | -| [triggerAction](#bind-a-triggeraction) | Global | Binds an action to the dialog that will clear the dialog stack and push itself onto the bottom of stack. Use `onSelectAction` option to override this default behavior. | -| [customAction](#bind-a-customaction) | Global | Binds a custom action to the bot that can process information or take action without affecting the dialog stack. Use `onSelectAction` option to customize the functionality of this action. | -[beginDialogAction](#bind-a-begindialogaction) | Contextual | Binds an action to the dialog that starts another dialog when it is triggered. The starting dialog will be pushed onto the stack and popped off once it ends. | -[reloadAction](#bind-a-reloadaction) | Contextual | Binds an action to the dialog that causes the dialog to reload when it is triggered. You can use `reloadAction` to handle user utterances like "start over." | -[cancelAction](#bind-a-cancelaction) | Contextual | Binds an action to the dialog that cancels the dialog when it is triggered. You can use `cancelAction` to handle user utterances like "cancel" or "nevermind." | -[endConversationAction](#bind-an-endconversationaction) | Contextual | Binds an action to the dialog that ends the conversation with the user when triggered. You can use `endConversationAction` to handle user utterances like "goodbye." | - -## Action precedence - -When a bot receives an utterance from a user, it checks the utterance against all the registered actions that are on the dialog stack. The matching starts from the top of the stack down to the bottom of the stack. If nothing matches, then it check the utterance against the `matches` option of all global actions. - -The action precedence is important in cases where you use the same command in difference contexts. For example, you can use the "Help" command for the bot as a general help. You can also use "Help" for each of the tasks but these help commands are contextual to each task. For a working sample that elaborates on this point, see [Respond to user input](bot-builder-nodejs-dialog-manage-conversation-flow.md#respond-to-user-input). - -## Bind actions to dialog - -Either user utterances or button clicks can *trigger* an action, which is associated with a [dialog](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html). -If *matches* is specified, the action will listen for the user to say a word or a phrase that triggers the action. The `matches` option can take a regular expression or the name of a [recognizer][RecognizeIntent]. -To bind the action to a button click, use [CardAction.dialogAction()][CardAction] to trigger the action. - -Actions are *chainable*, which allows you to bind as many actions to a dialog as you want. - -### Bind a triggerAction - -To bind a [triggerAction][triggerAction] to a dialog, do the following: - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Once triggered, will clear the dialog stack and pushes -// the 'orderDinner' dialog onto the bottom of stack. -.triggerAction({ - matches: /^order dinner$/i -}); -``` - -Binding a `triggerAction` to a dialog registers it to the bot. Once triggered, the `triggerAction` will clear the dialog stack and push the triggered dialog onto the stack. This action is best used to switch between [topic of conversation](bot-builder-nodejs-dialog-manage-conversation-flow.md#change-the-topic-of-conversation) or to allow users to request arbitrary stand-alone tasks. If you want to override the behavior where this action clears the dialog stack, add an `onSelectAction` option to the `triggerAction`. - -The code snippet below shows how to provide general help from a global context without clearing the dialog stack. - -```javascript -bot.dialog('help', function (session, args, next) { - //Send a help message - session.endDialog("Global help menu."); -}) -// Once triggered, will start a new dialog as specified by -// the 'onSelectAction' option. -.triggerAction({ - matches: /^help$/i, - onSelectAction: (session, args, next) => { - // Add the help dialog to the top of the dialog stack - // (override the default behavior of replacing the stack) - session.beginDialog(args.action, args); - } -}); -``` - -In this case, the `triggerAction` is attached to the `help` dialog itself (as opposed to the `orderDinner` dialog). The `onSelectAction` option allows you to start this dialog without clearing the dialog stack. This allows you to handle global requests such as "help", "about", "support", etc. Notice that the `onSelectAction` option explicitly calls the `session.beginDialog` method to start the triggered dialog. The ID of the triggered dialog is provided via the `args.action`. Do not hand code the dialog ID (e.g.: 'help') into this method otherwise you may get runtime errors. If you wish to trigger a contextual help message for the `orderDinner` task itself then consider attaching a `beginDialogAction` to the `orderDinner` dialog instead. - -### Bind a customAction - -Unlike other action types, `customAction` does not have any default action defined. It's up to you to define what this action does. The benefit of using `customAction` is that you have the option to process user requests without manipulating the dialog stack. When a `customAction` is triggered, the `onSelectAction` option can process the request without pushing new dialogs onto the stack. Once the action is completed, control is passed back to the dialog that is at the top of the stack and the bot can continue. - -You can use a `customAction` to provide general and quick action requests such as "What is the temperature outside right now?", "What time is it right now in Paris?", "Remind me to buy milk at 5pm today.", etc. These are general actions that the bot can perform with out manipulating the stack. - -Another key difference with `customAction` is that it binds to the bot as opposed to a dialog. - -The follow code sample shows how to bind a `customAction` to the `bot` listening for requests to set a reminder. - -```javascript -bot.customAction({ - matches: /remind|reminder/gi, - onSelectAction: (session, args, next) => { - // Set reminder... - session.send("Reminder is set."); - } -}) -``` - -### Bind a beginDialogAction - -Binding a `beginDialogAction` to a dialog will register the action to the dialog. This method will start another dialog when it is triggered. The behavior of this action is similar to calling the [beginDialog](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#begindialog) method. The new dialog is pushed onto the top of the dialog stack so it does not automatically end the current task. The current task is continued once the new dialog ends. - -The following code snippet shows how to bind a [beginDialogAction][beginDialogAction] to a dialog. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Once triggered, will start the 'showDinnerCart' dialog. -// Then, the waterfall will resumed from the step that was interrupted. -.beginDialogAction('showCartAction', 'showDinnerCart', { - matches: /^show cart$/i -}); - -// Show dinner items in cart -bot.dialog('showDinnerCart', function(session){ - for(var i = 1; i < session.conversationData.orders.length; i++){ - session.send(`You ordered: ${session.conversationData.orders[i].Description} for a total of $${session.conversationData.orders[i].Price}.`); - } - - // End this dialog - session.endDialog(`Your total is: $${session.conversationData.orders[0].Price}`); -}); -``` - -In cases where you need to pass additional arguments into the new dialog, you can add a [`dialogArgs`](https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.idialogactionoptions#dialogargs) option to the action. - -Using the sample above, you can modify it to accept arguments passed in via the `dialogArgs`. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Once triggered, will start the 'showDinnerCart' dialog. -// Then, the waterfall will resumed from the step that was interrupted. -.beginDialogAction('showCartAction', 'showDinnerCart', { - matches: /^show cart$/i, - dialogArgs: { - showTotal: true; - } -}); - -// Show dinner items in cart with the option to show total or not. -bot.dialog('showDinnerCart', function(session, args){ - for(var i = 1; i < session.conversationData.orders.length; i++){ - session.send(`You ordered: ${session.conversationData.orders[i].Description} for a total of $${session.conversationData.orders[i].Price}.`); - } - - if(args && args.showTotal){ - // End this dialog with total. - session.endDialog(`Your total is: $${session.conversationData.orders[0].Price}`); - } - else{ - session.endDialog(); // Ends without a message. - } -}); -``` - -### Bind a reloadAction - -Binding a `reloadAction` to a dialog will register it to the dialog. Binding this action to a dialog causes the dialog to restart when the action is triggered. Triggering this action is similar to calling the [replaceDialog](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#replacedialog) method. This is useful for implementing logic to handle user utterances like "start over" or to create [loops](bot-builder-nodejs-dialog-replace.md#repeat-an-action). - -The following code snippet shows how to bind a [reloadAction][reloadAction] to a dialog. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Once triggered, will restart the dialog. -.reloadAction('startOver', 'Ok, starting over.', { - matches: /^start over$/i -}); -``` - -In cases where you need to pass additional arguments into the reloaded dialog, you can add a [`dialogArgs`](https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.idialogactionoptions#dialogargs) option to the action. This option is passed into the `args` parameter. Rewriting the sample code above to receive an argument on a reload action will look something like this: - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - function(session, args, next){ - if(args && args.isReloaded){ - // Reload action was triggered. - } - - session.send("Lets order some dinner!"); - builder.Prompts.choice(session, "Dinner menu:", dinnerMenu); - } - //...other waterfall steps... -]) -// Once triggered, will restart the dialog. -.reloadAction('startOver', 'Ok, starting over.', { - matches: /^start over$/i, - dialogArgs: { - isReloaded: true; - } -}); -``` - -### Bind a cancelAction - -Binding a `cancelAction` will register it to the dialog. Once triggered, this action will abruptly end the dialog. Once the dialog ends, the parent dialog will resume with a resumed code indicating that it was `canceled`. This action allows you to handle utterances such as "nevermind" or "cancel." If you need to programmatically cancel a dialog instead, see [Cancel a dialog](bot-builder-nodejs-dialog-replace.md#cancel-a-dialog). For more information on *resumed code*, see [Prompt results](bot-builder-nodejs-dialog-prompt.md#prompt-results). - -The following code snippet shows how to bind a [cancelAction][cancelAction] to a dialog. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -//Once triggered, will end the dialog. -.cancelAction('cancelAction', 'Ok, cancel order.', { - matches: /^nevermind$|^cancel$|^cancel.*order/i -}); -``` - -### Bind an endConversationAction - -Binding an `endConversationAction` will register it to the dialog. Once triggered, this action ends the conversation with the user. Triggering this action is similar to calling the [endConversation](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#endconversation) method. Once a conversation ends, the Bot Framework SDK for Node.js will clear the dialog stack and persisted state data. For more information on persisted state data, see [Manage state data](bot-builder-nodejs-state.md). - -The following code snippet shows how to bind an [endConversationAction][endConversationAction] to a dialog. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Once triggered, will end the conversation. -.endConversationAction('endConversationAction', 'Ok, goodbye!', { - matches: /^goodbye$/i -}); -``` - -## Confirm interruptions - -Most, if not, all of these actions interrupt the normal flow of a conversation. Many are disruptive and should be handle with care. For example, the `triggerAction`, `cancelAction`, or the `endConversationAction` will clear the dialog stack. If the user made the mistake of triggering either of these actions, they will have to start the task over again. To make sure the user really intended to trigger these actions, you can add a `confirmPrompt` option to these actions. The `confirmPrompt` will ask if the user is sure about canceling or ending the current task. It allows the user to change their minds and continue the process. - -The code snippet below shows a [cancelAction][cancelAction] with a [confirmPrompt](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions#confirmprompt) to make sure the user really wants to cancel the order process. - -```javascript -// Order dinner. -bot.dialog('orderDinner', [ - //...waterfall steps... -]) -// Confirm before triggering the action. -// Once triggered, will end the dialog. -.cancelAction('cancelAction', 'Ok, cancel order.', { - matches: /^nevermind$|^cancel$|^cancel.*order/i, - confirmPrompt: "Are you sure?" -}); -``` - -Once this action is triggered, it will ask the user "Are you sure?" The user will have to answer "Yes" to go through with the action or "No" to cancel the action and continue where they were. - -## Next steps - -**Actions** allow you to anticipate user requests and allow the bot to handle those requests gracefully. Many of these actions are disruptive to the current conversation flow. If you want to allow users the ability to switch out and resume a conversation flow, you need to save the user state before switching out. Let's take a closer look at how to save user state and manage state data. - -> [!div class="nextstepaction"] -> [Manage state data](bot-builder-nodejs-state.md) - - -[triggerAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#triggeraction - -[cancelAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#cancelaction - -[reloadAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#reloadaction - -[beginDialogAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#begindialogaction - -[endConversationAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#endconversationaction - -[RecognizeIntent]: bot-builder-nodejs-recognize-intent-messages.md - -[CardAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.cardaction#dialogaction diff --git a/articles/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md b/articles/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md deleted file mode 100644 index 471f7557d..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md +++ /dev/null @@ -1,383 +0,0 @@ ---- -title: Manage a conversation flow with dialogs - Bot Service -description: Learn how to manage a conversation between a bot and a user with dialogs in the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage conversation flow with dialogs - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-manage-conversation-flow.md) -> - [Node.js](../nodejs/bot-builder-nodejs-dialog-manage-conversation-flow.md) - -Managing conversation flow is an essential task in building bots. A bot needs to be able to perform core tasks elegantly and handle interruptions gracefully. With the Bot Framework SDK for Node.js, you can manage conversation flow using dialogs. - -A dialog is like a function in a program. It is generally designed to perform a specific operation and it can be invoked as often as it is needed. You can chain multiple dialogs together to handle just about any conversation flow that you want your bot to handle. The Bot Framework SDK for Node.js includes built-in features such as [prompts](bot-builder-nodejs-dialog-prompt.md) and [waterfalls](bot-builder-nodejs-dialog-waterfall.md) to help you manage conversation flow. - -This article provides a series of examples to explain how to manage both simple conversation flows and complex conversation flows where your bot can handle interruptions and resume the flow gracefully using dialogs. The examples are based on the following scenarios: - -1. Your bot will take a dinner reservation. -2. Your bot can process "Help" request at any time during the reservation. -3. Your bot can process context-sensitive "Help" for current step of the reservation. -4. Your bot can handle multiple topics of conversation. - -## Manage conversation flow with a waterfall - -A [waterfall](bot-builder-nodejs-dialog-waterfall.md) is a dialog that allows the bot to easily walk a user through a series of tasks. In this example, the reservation bot asks the user a series of questions so that the bot can process the reservation request. The bot will prompt the user for the following information: - -1. Reservation date and time -2. Number of people in the party -3. Name of the person making the reservation - -The following code sample shows how to use a waterfall to guide the user through a series of prompts. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This is a dinner reservation bot that uses a waterfall technique to prompt users for input. -var bot = new builder.UniversalBot(connector, [ - function (session) { - session.send("Welcome to the dinner reservation."); - builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)"); - }, - function (session, results) { - session.dialogData.reservationDate = builder.EntityRecognizer.resolveTime([results.response]); - builder.Prompts.number(session, "How many people are in your party?"); - }, - function (session, results) { - session.dialogData.partySize = results.response; - builder.Prompts.text(session, "Whose name will this reservation be under?"); - }, - function (session, results) { - session.dialogData.reservationName = results.response; - - // Process request and display reservation details - session.send(`Reservation confirmed. Reservation details:
Date/Time: ${session.dialogData.reservationDate}
Party size: ${session.dialogData.partySize}
Reservation name: ${session.dialogData.reservationName}`); - session.endDialog(); - } -]).set('storage', inMemoryStorage); // Register in-memory storage -``` - -The core functionality of this bot occurs in the default dialog. The default dialog is defined when the bot is created: - -```javascript -var bot = new builder.UniversalBot(connector, [..waterfall steps..]); -``` - -Also, during this creation process, you can set which [data storage](bot-builder-nodejs-state.md) you want to use. For instance, to use the in-memory storage, you can set it as follows: - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); -var bot = new builder.UniversalBot(connector, [..waterfall steps..]).set('storage', inMemoryStorage); // Register in-memory storage -``` - -The default dialog is created as an array of functions that define the steps of the waterfall. In the example, there are four functions so the waterfall has four steps. Each step performs a single task and the results are processed in the next step. The process continues until the last step, where the reservation is confirmed and the dialog ends. - -The following screen shot shows the results of this bot running in the [Bot Framework Emulator](../bot-service-debug-emulator.md): - -![Manage conversation flow with waterfall](../media/bot-builder-nodejs-dialog-manage-conversation/waterfall-results.png) - -### Prompt user for input - -Each step of this example uses a prompt to ask the user for input. A prompt is a special type of dialog that asks for user input, waits for a response, and returns the response to the next step in the waterfall. See [Prompt users for input](bot-builder-nodejs-dialog-prompt.md) for information about the many different types of prompts you can use in your bot. - -In this example, the bot uses `Prompts.text()` to solicit a freeform response from the user in text format. The user can respond with any text and the bot must decide how to handle the response. `Prompts.time()` uses the [Chrono](https://github.com/wanasit/chrono) library to parse for date and time information from a string. This allows your bot to understand more natural language for specifying date and time. For example: "June 6th, 2017 at 9pm", "Today at 7:30pm", "next monday at 6pm", and so on. - -> [!TIP] -> The time that the user enters is converted to UTC time based upon the time zone of the server that hosts the bot. Since the server may be located in a different time zone than the user, be sure to take time zones into consideration. To convert date and time to the user's local time, consider asking the user what time zone they are in. - -## Manage a conversation flow with multiple dialogs - -Another technique for managing conversation flow is to use a combination of waterfall and multiple dialogs. The waterfall allows you to chain functions together in a dialog, while dialogs enable you to break a conversation into smaller pieces of functionality that can be reused at any time. - -For example, consider the dinner reservation bot. The following code sample shows the previous example rewritten to use waterfall and multiple dialogs. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This is a dinner reservation bot that uses multiple dialogs to prompt users for input. -var bot = new builder.UniversalBot(connector, [ - function (session) { - session.send("Welcome to the dinner reservation."); - session.beginDialog('askForDateTime'); - }, - function (session, results) { - session.dialogData.reservationDate = builder.EntityRecognizer.resolveTime([results.response]); - session.beginDialog('askForPartySize'); - }, - function (session, results) { - session.dialogData.partySize = results.response; - session.beginDialog('askForReserverName'); - }, - function (session, results) { - session.dialogData.reservationName = results.response; - - // Process request and display reservation details - session.send(`Reservation confirmed. Reservation details:
Date/Time: ${session.dialogData.reservationDate}
Party size: ${session.dialogData.partySize}
Reservation name: ${session.dialogData.reservationName}`); - session.endDialog(); - } -]).set('storage', inMemoryStorage); // Register in-memory storage - -// Dialog to ask for a date and time -bot.dialog('askForDateTime', [ - function (session) { - builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)"); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]); - -// Dialog to ask for number of people in the party -bot.dialog('askForPartySize', [ - function (session) { - builder.Prompts.text(session, "How many people are in your party?"); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]) - -// Dialog to ask for the reservation name. -bot.dialog('askForReserverName', [ - function (session) { - builder.Prompts.text(session, "Who's name will this reservation be under?"); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]); -``` - -The results from executing this bot are exactly the same as the previous bot where only waterfall is used. However, programmatically, there are two primary differences: - -1. The default dialog is dedicated to managing the flow of the conversation. -2. The task for each step of the conversation is being managed by a separate dialog. In this case, the bot needed three pieces of information so it prompts the user three times. Each prompt is now contained within its own dialog. - -With this technique, you can separate the conversation flow from the task logic. This allows the dialogs to be reused by different conversation flows if necessary. - -## Respond to user input - -In the process of guiding the user through a series of tasks, if the user has questions or wants to request additional information before answering, how would you handle those requests? For example, regardless of where the user is in the conversation, how would the bot respond if the user enters "Help", "Support" or "Cancel"? What if the user wants additional information about a step? What happens if the user changes their mind and wants to abandon the current task to start a completely different task? - -The Bot Framework SDK for Node.js allows a bot to listen for certain input within a global context or within a local context in the scope of the current dialog. These inputs are called [actions](bot-builder-nodejs-dialog-actions.md), which allow the bot to listen for user input based on a `matches` clause. It's up to the bot to decide how to react to specific user inputs. - -### Handle global action - -If you want your bot to be able to handle actions at any point in a conversation, use `triggerAction`. Triggers allow the bot to invoke a specific dialog when the input matches the specified term. For example, if you want to support a global "Help" option, you can create a help dialog and attach a `triggerAction` that listens for input matching "Help". - -The following code sample shows how to attach a `triggerAction` to a dialog to specify that the dialog should be invoked when the user enters "help". - -```javascript -// The dialog stack is cleared and this dialog is invoked when the user enters 'help'. -bot.dialog('help', function (session, args, next) { - session.endDialog("This is a bot that can help you make a dinner reservation.
Please say 'next' to continue"); -}) -.triggerAction({ - matches: /^help$/i, -}); -``` - -By default, when a `triggerAction` executes, the dialog stack is cleared and the triggered dialog becomes the new default dialog. In this example, when the `triggerAction` executes, the dialog stack is cleared and the `help` dialog is then added to the stack as the new default dialog. If this is not the desired behavior, you can add the `onSelectAction` option to the `triggerAction`. The `onSelectAction` option allows the bot to start a new dialog without clearing the dialog stack, which enables the conversation to be temporarily redirected and later resume where it left off. - -The following code sample shows how to use the `onSelectAction` option with `triggerAction` so that the `help` dialog is added to the existing dialog stack (and the dialog stack is not cleared). - -```javascript -bot.dialog('help', function (session, args, next) { - session.endDialog("This is a bot that can help you make a dinner reservation.
Please say 'next' to continue"); -}) -.triggerAction({ - matches: /^help$/i, - onSelectAction: (session, args, next) => { - // Add the help dialog to the dialog stack - // (override the default behavior of replacing the stack) - session.beginDialog(args.action, args); - } -}); -``` - -In this example, the `help` dialog takes control of the conversation when the user enters "help". Because the `triggerAction` contains the `onSelectAction` option, the `help` dialog is pushed onto the existing dialog stack and the stack is not cleared. When the `help` dialog ends, it is removed from the dialog stack and the conversation resumes from the point at which it was interrupted with the `help` command. - -### Handle contextual action - -In the previous example, the `help` dialog is invoked if the user enters "help" at any point in the conversation. As such, the dialog can only offer general help guidance, as there is no specific context for the user's help request. But, what if the user wants to request help regarding a specific point in the conversation? In that case, the `help` dialog must be triggered within the context of the current dialog. - -For example, consider the dinner reservation bot. What if a user wants to know the maximum party size when they are asked for the number of members in their party? To handle this scenario, you can attach a `beginDialogAction` to the `askForPartySize` dialog, listening for the user input "help". - -The following code sample shows how to attach context-sensitive help to a dialog using `beginDialogAction`. - -```javascript -// Dialog to ask for number of people in the party -bot.dialog('askForPartySize', [ - function (session) { - builder.Prompts.text(session, "How many people are in your party?"); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]) -.beginDialogAction('partySizeHelpAction', 'partySizeHelp', { matches: /^help$/i }); - -// Context Help dialog for party size -bot.dialog('partySizeHelp', function(session, args, next) { - var msg = "Party size help: Our restaurant can support party sizes up to 150 members."; - session.endDialog(msg); -}) -``` - -In this example, whenever the user enters "help", the bot will push the `partySizeHelp` dialog onto the stack. That dialog sends a help message to the user and then ends the dialog, returning control back to the `askForPartySize` dialog which reprompts the user for a party size. - -It is important to note that this context-sensitive help is only executed when the user is in the `askForPartySize` dialog. Otherwise, the general help message from the `triggerAction` will execute instead. In other words, the local `matches` clause always takes precedence over the global `matches` clause. For example, if a `beginDialogAction` matches for **help**, then the matches for **help** in the `triggerAction` will not be executed. For more information, see [Action precedence](bot-builder-nodejs-dialog-actions.md#action-precedence). - -### Change the topic of conversation - -By default, executing a `triggerAction` clears the dialog stack and resets the conversation, starting with the specified dialog. This behavior is often preferable when your bot needs to switch from one topic of conversation to another, such as if a user in the midst of booking a dinner reservation instead decides to order dinner to be delivered to their hotel room. - -The following example builds upon the previous one such that the bot allows the user to either make a dinner reservation or order dinner to be delivered. In this bot, the default dialog is a greeting dialog that presents two options to the user: `Dinner Reservation` and `Order Dinner`. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This bot enables users to either make a dinner reservation or order dinner. -var bot = new builder.UniversalBot(connector, function(session){ - var msg = "Welcome to the reservation bot. Please say `Dinner Reservation` or `Order Dinner`"; - session.send(msg); -}).set('storage', inMemoryStorage); // Register in-memory storage -``` - -The dinner reservation logic that was in the default dialog of the previous example is now in its own dialog called `dinnerReservation`. The flow of the `dinnerReservation` remains the same as the multiple dialog version discussed earlier. The only difference is that the dialog has a `triggerAction` attached to it. Notice that in this version, the `confirmPrompt` asks the user to confirm that they want to change the topic of conversation, before invoking the new dialog. It is good practice to include a `confirmPrompt` in scenarios like this because once the dialog stack is cleared, the user will be directed to the new topic of conversation, thereby abandoning the topic of conversation that was underway. - -```javascript -// This dialog helps the user make a dinner reservation. -bot.dialog('dinnerReservation', [ - function (session) { - session.send("Welcome to the dinner reservation."); - session.beginDialog('askForDateTime'); - }, - function (session, results) { - session.dialogData.reservationDate = builder.EntityRecognizer.resolveTime([results.response]); - session.beginDialog('askForPartySize'); - }, - function (session, results) { - session.dialogData.partySize = results.response; - session.beginDialog('askForReserverName'); - }, - function (session, results) { - session.dialogData.reservationName = results.response; - - // Process request and display reservation details - session.send(`Reservation confirmed. Reservation details:
Date/Time: ${session.dialogData.reservationDate}
Party size: ${session.dialogData.partySize}
Reservation name: ${session.dialogData.reservationName}`); - session.endDialog(); - } -]) -.triggerAction({ - matches: /^dinner reservation$/i, - confirmPrompt: "This will cancel your current request. Are you sure?" -}); -``` - -The second topic of conversation is defined in the `orderDinner` dialog using a waterfall. This dialog simply displays the dinner menu and prompts the user for a room number after the order is specified. A `triggerAction` is attached to the dialog to specify that it should be invoked when the user enters "order dinner" and to ensure that the user is prompted to confirm their selection, should they indicate a desire to change the topic of conversation. - -```javascript -// This dialog help the user order dinner to be delivered to their hotel room. -var dinnerMenu = { - "Potato Salad - $5.99": { - Description: "Potato Salad", - Price: 5.99 - }, - "Tuna Sandwich - $6.89": { - Description: "Tuna Sandwich", - Price: 6.89 - }, - "Clam Chowder - $4.50":{ - Description: "Clam Chowder", - Price: 4.50 - } -}; - -bot.dialog('orderDinner', [ - function(session){ - session.send("Lets order some dinner!"); - builder.Prompts.choice(session, "Dinner menu:", dinnerMenu); - }, - function (session, results) { - if (results.response) { - var order = dinnerMenu[results.response.entity]; - var msg = `You ordered: ${order.Description} for a total of $${order.Price}.`; - session.dialogData.order = order; - session.send(msg); - builder.Prompts.text(session, "What is your room number?"); - } - }, - function(session, results){ - if(results.response){ - session.dialogData.room = results.response; - var msg = `Thank you. Your order will be delivered to room #${session.dialogData.room}`; - session.endDialog(msg); - } - } -]) -.triggerAction({ - matches: /^order dinner$/i, - confirmPrompt: "This will cancel your order. Are you sure?" -}); -``` - -After the user starts a conversation and selects either `Dinner Reservation` or `Order Dinner`, they may change their mind at any time. For example, if the user is in the middle of making a dinner reservation and enters "order dinner," the bot will confirm by saying, "This will cancel your current request. Are you sure?". If the user types "no," then the request is canceled and the user can continue with the dinner reservation process. If the user types "yes," the bot will clear the dialog stack and transfer control of the conversation to the `orderDinner` dialog. - -## End conversation - -In the examples above, dialogs are closed by either using `session.endDialog` or `session.endDialogWithResult`, both of which end the dialog, remove it from the stack, and return control to the calling dialog. In situations where the user has reached the end of the conversation, you should use `session.endConversation` to indicate that the conversation is finished. - -The [`session.endConversation`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#endconversation) method ends a conversation and optionally sends a message to the user. For example, the `orderDinner` dialog in the previous example could end the conversation by using `session.endConversation`, as shown in the following code sample. - -```javascript -bot.dialog('orderDinner', [ - //...waterfall steps... - // Last step - function(session, results){ - if(results.response){ - session.dialogData.room = results.response; - var msg = `Thank you. Your order will be delivered to room #${session.dialogData.room}`; - session.endConversation(msg); - } - } -]); -``` - -Calling `session.endConversation` will end the conversation by clearing the dialog stack and resetting the [`session.conversationData`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#conversationdata) storage. For more information on data storage, see [Manage state data](bot-builder-nodejs-state.md). - -Calling `session.endConversation` is a logical thing to do when the user completes the conversation flow for which the bot is designed. You may also use `session.endConversation` to end the conversation in situations where the user enters "cancel" or "goodbye" in the midst of a conversation. To do so, simply attach an `endConversationAction` to the dialog and have this trigger listen for input matching "cancel" or "goodbye". - -The following code sample shows how to attach an `endConversationAction` to a dialog to end the conversation if the user enters "cancel" or "goodbye". - -```javascript -bot.dialog('dinnerOrder', [ - //...waterfall steps... -]) -.endConversationAction( - "endOrderDinner", "Ok. Goodbye.", - { - matches: /^cancel$|^goodbye$/i, - confirmPrompt: "This will cancel your order. Are you sure?" - } -); -``` - -Since ending a conversation with `session.endConversation` or `endConversationAction` will clear the dialog stack and force the user to start over, you should include a `confirmPrompt` to ensure that the user really wants to do so. - -## Next steps - -In this article, you explore ways to manage conversations that are sequential in nature. What if you want to repeat a dialog or use looping pattern in your conversation? Let's see how you can do that by replacing dialogs on the stack. - -> [!div class="nextstepaction"] -> [Replace dialogs](bot-builder-nodejs-dialog-replace.md) - diff --git a/articles/nodejs/bot-builder-nodejs-dialog-overview.md b/articles/nodejs/bot-builder-nodejs-dialog-overview.md deleted file mode 100644 index 869534a97..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-overview.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Dialogs overview (v3 JS) - Bot Service -description: Learn how to use dialogs within the Bot Framework SDK for Node.js to model conversations and manage conversation flow. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Dialogs in the Bot Framework SDK for Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-dialogs.md) -> - [Node.js](../nodejs/bot-builder-nodejs-dialog-overview.md) - -Dialogs in the Bot Framework SDK for Node.js allow you to model conversations and manage conversation flow. A bot communicates with a user via conversations. Conversations are organized into dialogs. Dialogs can contain waterfall steps, and prompts. As the user interacts with the bot, the bot will start, stop, and switch between various dialogs in response to user messages. Understanding how dialogs work is key to successfully designing and creating great bots. - -This article introduces dialog concepts. After you read this article, then follow the links in the [Next steps](#next-steps) section to dive deeper into these concepts. - -## Conversations through dialogs - -Bot Framework SDK for Node.js defines a conversation as the communication between a bot and a user through one or more dialogs. A dialog, at its most basic level, is a reusable module that performs an operation or collects information from a user. You can encapsulate the complex logic of your bot in reusable dialog code. - -A conversation can be structured and changed in many ways: - -- It can originate from your [default dialog](#default-dialog). -- It can be redirected from one dialog to another. -- It can be resumed. -- It can follow a [waterfall](bot-builder-nodejs-dialog-waterfall.md) pattern, which guides the user through a series of steps or [prompts](bot-builder-nodejs-dialog-prompt.md) the user with a series of questions. -- It can use [actions](bot-builder-nodejs-dialog-actions.md) that listen for words or phrases that trigger a different dialog. - -You can think of a conversation like a parent to dialogs. As such, a conversation contains a *dialog stack* and maintain its own set of state data; namely, the `conversationData` and the `privateConversationData`. A dialog, on the other hand, maintains the `dialogData`. For more information on state data, see [Manage state data](bot-builder-nodejs-state.md). - -## Dialog stack - -A bot interacts with a user through a series of dialogs that are maintained on a dialog stack. Dialogs are pushed on and popped off the stack in the course of a conversation. The stack works like a normal LIFO stack; meaning, the last dialog added will be the first one to complete. Once a dialog completes then control is returned to the previous dialog on the stack. - -When a bot conversation first starts or when a conversation ends, the dialog stack is empty. At this point, when the a user sends a message to the bot, the bot will respond with the *default dialog*. - -## Default dialog - -Prior to Bot Framework version 3.5, a *root* dialog is defined by adding a dialog named `/`, which lead to naming conventions similar to that of URLs. This naming convention wasn't appropriate to naming dialogs. - -> [!NOTE] -> Starting with version 3.5 of the Bot Framework, the *default dialog* is registered as the second parameter in the constructor of [`UniversalBot`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html#constructor). - -The following code snippet shows how to define the default dialog when creating the `UniversalBot` object. - -```javascript -var bot = new builder.UniversalBot(connector, [ - //...Default dialog waterfall steps... - ]); -``` - -The default dialog runs whenever the dialog stack is empty and no other dialog is [triggered](bot-builder-nodejs-dialog-actions.md) via LUIS or another [recognizer](bot-builder-nodejs-recognize-intent-messages.md). As the default dialog is the bot's first response to the user, the default dialog should provide some contextual information to the user, such as a list of available commands or an overview of what the bot can do. - -## Dialog handlers - -The dialog handler manages the flow of a conversation. To progress through a conversation, the dialog handler directs the flow by starting and ending dialogs. - -## Starting and ending dialogs - -To start a new dialog (and push it onto the stack), use [`session.beginDialog()`](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#begindialog). To end a dialog (and remove it from the stack, returning control to the calling dialog), use either [`session.endDialog()`](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#enddialog) or [`session.endDialogWithResult()`](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#enddialogwithresult). - -## Using waterfalls and prompts - -[Waterfall](bot-builder-nodejs-dialog-waterfall.md) is a simple way to model and manage conversation flow. A waterfall contains a sequence of steps. In each step, you can either complete an action on behalf of the user or [prompt](bot-builder-nodejs-dialog-prompt.md) the user for information. - -A waterfall is implemented using a dialog that's made up of a collection of functions. Each function defines a step in the waterfall. The following code sample shows a simple conversation that uses a two step waterfall to prompt the user for their name and greet them by name. - -```javascript -// Ask the user for their name and greet them by name. -bot.dialog('greetings', [ - function (session) { - builder.Prompts.text(session, 'Hi! What is your name?'); - }, - function (session, results) { - session.endDialog(`Hello ${results.response}!`); - } -]); -``` - -When a bot reaches the end of the waterfall without ending the dialog, the next message from the user will restart that dialog at step one of the waterfall. This may lead to frustrations as the user may feel like they are trapped in a loop. To avoid this situation, when a conversation or dialog has come to an end, it is best practice to explicitly call `endDialog`, `endDialogWithResult`, or `endConversation`. - -## Next steps - -To dive deeper into dialogs, it is important to understand how waterfall pattern works and how to use it to guide users through a process. - -> [!div class="nextstepaction"] -> [Define conversation steps with waterfalls](bot-builder-nodejs-dialog-waterfall.md) diff --git a/articles/nodejs/bot-builder-nodejs-dialog-prompt.md b/articles/nodejs/bot-builder-nodejs-dialog-prompt.md deleted file mode 100644 index 736f50ed7..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-prompt.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -title: Prompt for user input - Bot Service -description: Learn how to use prompts to collect user input with the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Prompt for user input - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -The Bot Framework SDK for Node.js provides a set of built-in prompts to simplify collecting inputs from a user. - -A *prompt* is used whenever a bot needs input from the user. You can use prompts to ask a user for a series of inputs by chaining the prompts in a waterfall. You can use prompts in conjunction with [waterfall](bot-builder-nodejs-dialog-waterfall.md) to help you [manage conversation flow](bot-builder-nodejs-manage-conversation-flow.md) in your bot. - -This article will help you understand how prompts work and how you can use them to collect information from users. - -## Prompts and responses - -Whenever you need input from a user, you can send a prompt, wait for the user to respond with input, and then process the input and send a response to the user. - -The following code sample prompts the user for their name and responds with a greeting message. - -```javascript -bot.dialog('greetings', [ - // Step 1 - function (session) { - builder.Prompts.text(session, 'Hi! What is your name?'); - }, - // Step 2 - function (session, results) { - session.endDialog(`Hello ${results.response}!`); - } -]); -``` - -Using this basic construct, you can model your conversation flow by adding as many prompts and responses as your bot requires. - -## Prompt results - -Built-in prompts are implemented as [dialogs](bot-builder-nodejs-dialog-overview.md) that return the user's response in the `results.response` field. For JSON objects, responses are returned in the `results.response.entity` field. Any type of [dialog handler](bot-builder-nodejs-dialog-overview.md#dialog-handlers) can receive the result of a prompt. Once the bot receives a response, it can consume it or pass it back to the calling dialog by calling the [`session.endDialogWithResult`][EndDialogWithResult] method. - -The following code sample shows how to return a prompt result to the calling dialog by using the `session.endDialogWithResult` method. In this example, the `greetings` dialog uses the prompt result that the `askName` dialog returns to greet the user by name. - -```javascript -// Ask the user for their name and greet them by name. -bot.dialog('greetings', [ - function (session) { - session.beginDialog('askName'); - }, - function (session, results) { - session.endDialog(`Hello ${results.response}!`); - } -]); -bot.dialog('askName', [ - function (session) { - builder.Prompts.text(session, 'Hi! What is your name?'); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]); -``` - -## Prompt types -The Bot Framework SDK for Node.js includes several different types of built-in prompts. - -|**Prompt type** | **Description** | -| ------------------ | --------------- | -|[Prompts.text](#promptstext) | Asks the user to enter a string of text. | -|[Prompts.confirm](#promptsconfirm) | Asks the user to confirm an action.| -|[Prompts.number](#promptsnumber) | Asks the user to enter a number. | -|[Prompts.time](#promptstime) | Asks the user for a time or date/time. | -|[Prompts.choice](#promptschoice) | Asks the user to choose from a list of options. | -|[Prompts.attachment](#promptsattachment) | Asks the user to upload a picture or video.| - -The following sections provide additional details about each type of prompt. - -### Prompts.text - -Use the [Prompts.text()][PromptsText] method to ask the user for a **string of text**. The prompt returns the user's response as an [IPromptTextResult][IPromptTextResult]. - -```javascript -builder.Prompts.text(session, "What is your name?"); -``` - -### Prompts.confirm - -Use the [Prompts.confirm()][PromptsConfirm] method to ask the user to confirm an action with a **yes/no** response. The prompt returns the user's response as an [IPromptConfirmResult](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptconfirmresult.html). - -```javascript -builder.Prompts.confirm(session, "Are you sure you wish to cancel your order?"); -``` - -### Prompts.number - -Use the [Prompts.number()][PromptsNumber] method to ask the user for a **number**. The prompt returns the user's response as an [IPromptNumberResult](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptnumberresult.html). - -```javascript -builder.Prompts.number(session, "How many would you like to order?"); -``` - -### Prompts.time - -Use the [Prompts.time()][PromptsTime] method to ask the user for a **time** or **date/time**. The prompt returns the user's response as an [IPromptTimeResult](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iprompttimeresult.html). The framework uses the [Chrono](https://github.com/wanasit/chrono) library to parse the user's response and supports both relative responses (e.g., "in 5 minutes") and non-relative responses (e.g., "June 6th at 2pm"). - -The [results.response](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iprompttimeresult.html#response) field, which represents the user's response, contains an [entity](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ientity.html) object that specifies the date and time. To resolve the date and time into a JavaScript `Date` object, use the [EntityRecognizer.resolveTime()](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.entityrecognizer.html#resolvetime) method. - -> [!TIP] -> The time that the user enters is converted to UTC time based upon the time zone of the server that hosts the bot. Since the server may be located in a different time zone than the user, be sure to take time zones into consideration. To convert date and time to the user's local time, consider asking the user what time zone they are in. - -```javascript -bot.dialog('createAlarm', [ - function (session) { - session.dialogData.alarm = {}; - builder.Prompts.text(session, "What would you like to name this alarm?"); - }, - function (session, results, next) { - if (results.response) { - session.dialogData.name = results.response; - builder.Prompts.time(session, "What time would you like to set an alarm for?"); - } else { - next(); - } - }, - function (session, results) { - if (results.response) { - session.dialogData.time = builder.EntityRecognizer.resolveTime([results.response]); - } - - // Return alarm to caller - if (session.dialogData.name && session.dialogData.time) { - session.endDialogWithResult({ - response: { name: session.dialogData.name, time: session.dialogData.time } - }); - } else { - session.endDialogWithResult({ - resumed: builder.ResumeReason.notCompleted - }); - } - } -]); -``` - -### Prompts.choice - -Use the [Prompts.choice()][PromptsChoice] method to ask the user to **choose from a list of options**. The user can convey their selection either by entering the number associated with the option that they choose or by entering the name of the option that they choose. Both full and partial matches of the option's name are supported. The prompt returns the user's response as an [IPromptChoiceResult](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptchoiceresult.html). - -To specify the style of the list that is presented to the user, set the [IPromptOptions.listStyle](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptoptions.html#liststyle) property. The following table shows the `ListStyle` enumeration values for this property. - - -The `ListStyle` enum values are as follows: - -| Index | Name | Description | -| ---- | ---- | ---- | -| 0 | none | No list is rendered. This is used when the list is included as part of the prompt. | -| 1 | inline | Choices are rendered as an inline list of the form "1. red, 2. green, or 3. blue". | -| 2 | list | Choices are rendered as a numbered list. | -| 3 | button | Choices are rendered as buttons for channels that support buttons. For other channels they will be rendered as text. | -| 4 | auto | The style is selected automatically based on the channel and number of options. | - -You can access this enum from the `builder` object or you can provide an index to choose a `ListStyle`. For example, both statements in the following code snippet accomplish the same thing. - -```javascript -// ListStyle passed in as Enum -builder.Prompts.choice(session, "Which color?", "red|green|blue", { listStyle: builder.ListStyle.button }); - -// ListStyle passed in as index -builder.Prompts.choice(session, "Which color?", "red|green|blue", { listStyle: 3 }); -``` - -To specify the list of options, you can use a pipe-delimited (`|`) string, an array of strings, or an object map. - -A pipe-delimited string: - -```javascript -builder.Prompts.choice(session, "Which color?", "red|green|blue"); -``` - -An array of strings: - -```javascript -builder.Prompts.choice(session, "Which color?", ["red","green","blue"]); -``` - -An object map: - -```javascript -var salesData = { - "west": { - units: 200, - total: "$6,000" - }, - "central": { - units: 100, - total: "$3,000" - }, - "east": { - units: 300, - total: "$9,000" - } -}; - -bot.dialog('getSalesData', [ - function (session) { - builder.Prompts.choice(session, "Which region would you like sales for?", salesData); - }, - function (session, results) { - if (results.response) { - var region = salesData[results.response.entity]; - session.send(`We sold ${region.units} units for a total of ${region.total}.`); - } else { - session.send("OK"); - } - } -]); -``` - -### Prompts.attachment - -Use the [Prompts.attachment()][PromptsAttachment] method to ask the user to upload a file such an image or video. The prompt returns the user's response as an [IPromptAttachmentResult](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptattachmentresult.html). - -```javascript -builder.Prompts.attachment(session, "Upload a picture for me to transform."); -``` - -## Next steps - -Now that you know how to step users through a waterfall and prompt them for information, lets take a look at ways to help you better manage the conversation flow. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-nodejs-dialog-manage-conversation-flow.md) - - -[SendAttachments]: bot-builder-nodejs-send-receive-attachments.md -[SendCardWithButtons]: bot-builder-nodejs-send-rich-cards.md -[RecognizeUserIntent]: bot-builder-nodejs-recognize-intent-messages.md -[SaveUserData]: bot-builder-nodejs-save-user-data.md - -[UniversalBot]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html -[ChatConnector]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html -[Session]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session - - -[SendTyping]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#sendtyping - -[EndDialogWithResult]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#enddialogwithresult - -[IPromptResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptresult.html - -[Result_Response]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptresult.html#response - -[ResumeReason]: https://docs.botframework.com/node/builder/chat-reference/enums/_botbuilder_d_.resumereason.html - -[Result_Resumed]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptresult.html#resumed - -[entity]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ientity.html - -[ResolveTime]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.entityrecognizer.html#resolvetime - -[PromptsRef]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html - -[PromptsText]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#text - -[IPromptTextResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iprompttextresult.html - -[PromptsConfirm]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#confirm - -[IPromptConfirmResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptconfirmresult.html - -[PromptsNumber]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#number - -[IPromptNumberResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptnumberresult.html - -[PromptsTime]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#time - -[IPromptTimeResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iprompttimeresult.html - -[PromptsChoice]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#choice - -[IPromptChoiceResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptchoiceresult.html - -[PromptsAttachment]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#attachment - -[IPromptAttachmentResult]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptattachmentresult.html diff --git a/articles/nodejs/bot-builder-nodejs-dialog-replace.md b/articles/nodejs/bot-builder-nodejs-dialog-replace.md deleted file mode 100644 index 82c15bb5b..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-replace.md +++ /dev/null @@ -1,335 +0,0 @@ ---- -title: Replace dialogs - Bot Service -description: Learn how to replace dialogs to re-prompt for input and manage conversation flow using the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Replace dialogs - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -The ability to replace a dialog can be useful when you need to validate user input or repeat an action during the course of a conversation. With the Bot Framework SDK for Node.js, you can replace a dialog by using the [`session.replaceDialog`](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#replacedialog) method. This method enables you to end the current dialog and replace it with a new dialog without returning to the caller. - -## Create custom prompts to validate input - -The Bot Framework SDK for Node.js includes input validation for some types of [prompts](bot-builder-nodejs-dialog-prompt.md) such as `Prompts.time` and `Prompts.choice`. To validate text input that you receive in response to `Prompts.text`, you must create your own validation logic and custom prompts. - -You may want to validate an input if the input must comply with a certain value, pattern, range, or criteria that you define. If an input fails validation, the bot can prompt the user for that information again by using the `session.replaceDialog` method. - -The following code sample shows how to create a custom prompt to validate user input for a phone number. - -```javascript -// This dialog prompts the user for a phone number. -// It will re-prompt the user if the input does not match a pattern for phone number. -bot.dialog('phonePrompt', [ - function (session, args) { - if (args && args.reprompt) { - builder.Prompts.text(session, "Enter the number using a format of either: '(555) 123-4567' or '555-123-4567' or '5551234567'") - } else { - builder.Prompts.text(session, "What's your phone number?"); - } - }, - function (session, results) { - var matched = results.response.match(/\d+/g); - var number = matched ? matched.join('') : ''; - if (number.length == 10 || number.length == 11) { - session.userData.phoneNumber = number; // Save the number. - session.endDialogWithResult({ response: number }); - } else { - // Repeat the dialog - session.replaceDialog('phonePrompt', { reprompt: true }); - } - } -]); -``` - -In this example, the user is initially prompted to provide their phone number. The validation logic uses a regular expression that matches a range of digits from the user input. If the input contains 10 or 11 digits, then that number is returned in the response. Otherwise, the `session.replaceDialog` method is executed to repeat the `phonePrompt` dialog, which prompts the user for input again, this time providing more specific guidance regarding the format of input that is expected. - -When you call the `session.replaceDialog` method, you specify the name of the dialog to repeat and an arguments list. In this example, the arguments list contains `{ reprompt: true }`, which enables the bot to provide different prompt messages depending on whether it is an initial prompt or a reprompt, but you can specify whatever arguments your bot may require. - -## Repeat an action - -There may be times in the course of a conversation where you want to repeat a dialog to enable the user to complete a certain action multiple times. For example, if your bot offers a variety of services, you might initially display the menu of services, walk the user through the process of requesting a service, and then display the menu of services again, thereby enabling the user to request another service. To achieve this, you can use the [`session.replaceDialog`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#replacedialog) method to display the menu of services again, rather than ending the conversation with the ['session.endConversation`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#endconversation) method. - -The following example shows how to use the `session.replaceDialog` method to implement this type of scenario. First, the menu of services is defined: - -```javascript -// Main menu -var menuItems = { - "Order dinner": { - item: "orderDinner" - }, - "Dinner reservation": { - item: "dinnerReservation" - }, - "Schedule shuttle": { - item: "scheduleShuttle" - }, - "Request wake-up call": { - item: "wakeupCall" - }, -} -``` - -The `mainMenu` dialog is invoked by the default dialog, so the menu will be presented to the user at the beginning of the conversation. Additionally, a [`triggerAction`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#triggeraction) is attached to the `mainMenu` dialog so that the menu will also be presented any time the user input is "main menu". When the user is presented with the menu and selects an option, the dialog that corresponds to the user's selection is invoked by using the `session.beginDialog` method. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This is a reservation bot that has a menu of offerings. -var bot = new builder.UniversalBot(connector, [ - function(session){ - session.send("Welcome to Contoso Hotel and Resort."); - session.beginDialog("mainMenu"); - } -]).set('storage', inMemoryStorage); // Register in-memory storage - -// Display the main menu and start a new request depending on user input. -bot.dialog("mainMenu", [ - function(session){ - builder.Prompts.choice(session, "Main Menu:", menuItems); - }, - function(session, results){ - if(results.response){ - session.beginDialog(menuItems[results.response.entity].item); - } - } -]) -.triggerAction({ - // The user can request this at any time. - // Once triggered, it clears the stack and prompts the main menu again. - matches: /^main menu$/i, - confirmPrompt: "This will cancel your request. Are you sure?" -}); -``` - -In this example, if the user chooses option 1 to order dinner to be delivered to their room, the `orderDinner` dialog will be invoked and will walk the user through the process of ordering dinner. At the end of the process, the bot will confirm the order and display the main menu again by using the `session.replaceDialog` method. - -```javascript -// Menu: "Order dinner" -// This dialog allows user to order dinner to be delivered to their hotel room. -bot.dialog('orderDinner', [ - function(session){ - session.send("Lets order some dinner!"); - builder.Prompts.choice(session, "Dinner menu:", dinnerMenu); - }, - function (session, results) { - if (results.response) { - var order = dinnerMenu[results.response.entity]; - var msg = `You ordered: %(Description)s for a total of $${order.Price}.`; - session.dialogData.order = order; - session.send(msg); - builder.Prompts.text(session, "What is your room number?"); - } - }, - function(session, results){ - if(results.response){ - session.dialogData.room = results.response; - var msg = `Thank you. Your order will be delivered to room #${results.response}.`; - session.send(msg); - session.replaceDialog("mainMenu"); // Display the menu again. - } - } -]) -.reloadAction( - "restartOrderDinner", "Ok. Let's start over.", - { - matches: /^start over$/i - } -) -.cancelAction( - "cancelOrder", "Type 'Main Menu' to continue.", - { - matches: /^cancel$/i, - confirmPrompt: "This will cancel your order. Are you sure?" - } -); -``` - -Two triggers are attached to the `orderDinner` dialog to enable the user to "start over" or "cancel" the order at any time during the ordering process. - -The first trigger is [`reloadAction`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#reloadaction), which allows the user to start the order process over again by sending the input "start over". When the trigger matches the utterance "start over", the `reloadAction` restarts the dialog from the beginning. - -The second trigger is [`cancelAction`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#cancelaction), which allows the user to abort the order process completely by sending the input "cancel". This trigger does not automatically display the main menu again, but instead sends a message that tells the user what to do next by saying "Type 'Main Menu' to continue." - -### Dialog loops - -In the example above, the user can only select one item per order. That is, if the user wanted to order two items from the menu, they would have to complete the entire ordering process for the first item and then repeat the entire ordering process again for the second item. - -The following example shows how to improve upon the previous bot by refactoring the dinner menu into a separate dialog. Doing so enables the bot to repeat the dinner menu in a loop and therefore allows the user to select multiple items within a single order. - -First, add a "Check out" option to the menu. This option will allow the user to exit the item selection process and continue with the check out process. - -```javascript -// The dinner menu -var dinnerMenu = { - //...other menu items..., - "Check out": { - Description: "Check out", - Price: 0 // Order total. Updated as items are added to order. - } -}; -``` - -Next, refactor the order prompt into its own dialog so that the bot can repeat the menu and allow user to add multiple items to their order. - -```javascript -// Add dinner items to the list by repeating this dialog until the user says `check out`. -bot.dialog("addDinnerItem", [ - function(session, args){ - if(args && args.reprompt){ - session.send("What else would you like to have for dinner tonight?"); - } - else{ - // New order - // Using the conversationData to store the orders - session.conversationData.orders = new Array(); - session.conversationData.orders.push({ - Description: "Check out", - Price: 0 - }) - } - builder.Prompts.choice(session, "Dinner menu:", dinnerMenu); - }, - function(session, results){ - if(results.response){ - if(results.response.entity.match(/^check out$/i)){ - session.endDialog("Checking out..."); - } - else { - var order = dinnerMenu[results.response.entity]; - session.conversationData.orders[0].Price += order.Price; // Add to total. - var msg = `You ordered: ${order.Description} for a total of $${order.Price}.`; - session.send(msg); - session.conversationData.orders.push(order); - session.replaceDialog("addDinnerItem", { reprompt: true }); // Repeat dinner menu - } - } - } -]) -.reloadAction( - "restartOrderDinner", "Ok. Let's start over.", - { - matches: /^start over$/i - } -); -``` - -In this example, orders are stored in a bot data store that is scoped to the current conversation, `session.conversationData.orders`. For every new order, the variable is re-initialized with a new array and for every item the user chooses, the bot adds that item to the `orders` array and adds the price to the total, which is stored in the checkout's `Price` variable. When the user finishes selecting items for their order, they can say "check out" and then continue with the remainder of the order process. - -> [!NOTE] -> For more information about bot data storage, see [Manage state data](bot-builder-nodejs-state.md). - -Finally, update the second step of the waterfall within the `orderDinner` dialog to process the order with confirmation. - -```javascript -// Menu: "Order dinner" -// This dialog allows user to order dinner and have it delivered to their room. -bot.dialog('orderDinner', [ - function(session){ - session.send("Lets order some dinner!"); - session.beginDialog("addDinnerItem"); - }, - function (session, results) { - if (results.response) { - // Display itemize order with price total. - for(var i = 1; i < session.conversationData.orders.length; i++){ - session.send(`You ordered: ${session.conversationData.orders[i].Description} for a total of $${session.conversationData.orders[i].Price}.`); - } - session.send(`Your total is: $${session.conversationData.orders[0].Price}`); - - // Continue with the check out process. - builder.Prompts.text(session, "What is your room number?"); - } - }, - function(session, results){ - if(results.response){ - session.dialogData.room = results.response; - var msg = `Thank you. Your order will be delivered to room #${results.response}`; - session.send(msg); - session.replaceDialog("mainMenu"); - } - } -]) -//...attached triggers... -; -``` - -## Cancel a dialog - -While the `session.replaceDialog` method can be used to replace the *current* dialog with a new one, it cannot be used to replace a dialog that is located further down the dialog stack. To replace a dialog within the dialog stack that is not the *current* dialog, use the [`session.cancelDialog`](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#canceldialog) method instead. - -The `session.cancelDialog` method can be used to end a dialog regardless of where it exists in the dialog stack and optionally invoke a new dialog in its place. To call the `session.cancelDialog` method, specify the ID of the dialog to cancel and optionally, specify the ID of the dialog to invoke in its place. For example, this code snippet cancels the `orderDinner` dialog and replaces it with the `mainMenu` dialog: - -```javascript -session.cancelDialog('orderDinner', 'mainMenu'); -``` - -When the `session.cancelDialog` method is called, the dialog stack will be searched backwards and the first occurrence of that dialog will be canceled, causing that dialog and its child dialogs (if any) to be removed from the dialog stack. Control will then be returned to the calling dialog, which can check for a [`results.resumed`](http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ipromptresult.html#resumed) code equal to [`ResumeReason.notCompleted`](http://docs.botframework.com/node/builder/chat-reference/enums/_botbuilder_d_.resumereason.html#notcompleted) to detect the cancellation. - -As an alternative to specifying the ID of the dialog to cancel when you call the `session.cancelDialog` method, you can instead specify the zero-based index of the dialog to cancel, where the index represents the dialog's position in the dialog stack. For example, the following code snippet terminates the currently active dialog (index = 0) and starts the `mainMenu` dialog in its place. The `mainMenu` dialog is invoked at position 0 of the dialog stack, thereby becoming the new default dialog. - -```javascript -session.cancelDialog(0, 'mainMenu'); -``` - -Consider the sample that is discussed in [dialog loops](#dialog-loops) above. When the user reaches the item selection menu, that dialog (`addDinnerItem`) is the fourth dialog in the dialog stack: `[default dialog, mainMenu, orderDinner, addDinnerItem]`. How can you enable the user to cancel their order from within the `addDinnerItem` dialog? If you attach a `cancelAction` trigger to the `addDinnerItem` dialog, it will only return the user back to the previous dialog (`orderDinner`), which will send the user right back into the `addDinnerItem` dialog. - -This is where the `session.cancelDialog` method is useful. Starting with the [dialog loops example](#dialog-loops), add "Cancel order" as an explicit option within the dinner menu. - -```javascript -// The dinner menu -var dinnerMenu = { - //...other menu items..., - "Check out": { - Description: "Check out", - Price: 0 // Order total. Updated as items are added to order. - }, - "Cancel order": { // Cancel the order and back to Main Menu - Description: "Cancel order", - Price: 0 - } -}; -``` - -Then, update the `addDinnerItem` dialog to check for a "cancel order" request. If "cancel" is detected, use the `session.cancelDialog` method to cancel the default dialog (i.e., the dialog at index 0 of the stack) and invoke the `mainMenu` dialog in its place. - -```javascript -// Add dinner items to the list by repeating this dialog until the user says `check out`. -bot.dialog("addDinnerItem", [ - //...waterfall steps..., - // Last step - function(session, results){ - if(results.response){ - if(results.response.entity.match(/^check out$/i)){ - session.endDialog("Checking out..."); - } - else if(results.response.entity.match(/^cancel/i)){ - // Cancel the order and start "mainMenu" dialog. - session.cancelDialog(0, "mainMenu"); - } - else { - //...add item to list and prompt again... - session.replaceDialog("addDinnerItem", { reprompt: true }); // Repeat dinner menu. - } - } - } -]) -//...attached triggers... -; -``` - -By using the `session.cancelDialog` method in this way, you can implement whatever conversation flow your bot requires. - -## Next steps - -As you can see, to replace dialogs on the stack, you use various types of **actions** to accomplish that task. Actions gives you great flexibilities in managing conversation flow. Let's take a closer look at **actions** and how to better handle user actions. - -> [!div class="nextstepaction"] -> [Handle user actions](bot-builder-nodejs-dialog-actions.md) diff --git a/articles/nodejs/bot-builder-nodejs-dialog-waterfall.md b/articles/nodejs/bot-builder-nodejs-dialog-waterfall.md deleted file mode 100644 index 72f30ddd5..000000000 --- a/articles/nodejs/bot-builder-nodejs-dialog-waterfall.md +++ /dev/null @@ -1,200 +0,0 @@ ---- -title: Define conversation steps with waterfalls - Bot Service -description: Learn how to use waterfalls to define the steps of a conversation with the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Define conversation steps with waterfalls - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -A conversation is a series of messages exchanged between user and bot. When the bot's objective is to lead the user through a series of steps, you can use a waterfall to define the steps of the conversation. - -A waterfall is a specific implementation of a [dialog](bot-builder-nodejs-dialog-overview.md) that is most commonly used to collect information from the user or guide the user through a series of tasks. The tasks are implemented as an array of functions where the results of the first function are passed as input into the next function, and so on. Each function typically represents one step in the overall process. At each step, the bot prompts the user for input, waits for a response, and then passes the result to the next step. - -This article will help you understand how a waterfall works and how you can use it to [manage conversation flow](bot-builder-nodejs-dialog-manage-conversation.md). - -## Conversation steps - -A conversation typically involves several prompt/response exchanges between the user and the bot. Each prompt/response exchange is a step forward in the conversation. You can create a conversation using a waterfall with as few as two steps. - -For example, consider the following `greetings` dialog. The first step of the waterfall prompts for the user's name and the second step uses the response to greet the user by name. - -```javascript -bot.dialog('greetings', [ - // Step 1 - function (session) { - builder.Prompts.text(session, 'Hi! What is your name?'); - }, - // Step 2 - function (session, results) { - session.endDialog(`Hello ${results.response}!`); - } -]); -``` - -What makes this possible is the use of prompts. The Bot Framework SDK for Node.js provides several different types of built-in [prompts](bot-builder-nodejs-dialog-prompt.md) that you can use to ask the user for various types of information. - -The following sample code shows a dialog that uses prompts to collect various pieces of information from the user throughout a 4-step waterfall. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This is a dinner reservation bot that uses a waterfall technique to prompt users for input. -var bot = new builder.UniversalBot(connector, [ - function (session) { - session.send("Welcome to the dinner reservation."); - builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)"); - }, - function (session, results) { - session.dialogData.reservationDate = builder.EntityRecognizer.resolveTime([results.response]); - builder.Prompts.text(session, "How many people are in your party?"); - }, - function (session, results) { - session.dialogData.partySize = results.response; - builder.Prompts.text(session, "Whose name will this reservation be under?"); - }, - function (session, results) { - session.dialogData.reservationName = results.response; - - // Process request and display reservation details - session.send(`Reservation confirmed. Reservation details:
Date/Time: ${session.dialogData.reservationDate}
Party size: ${session.dialogData.partySize}
Reservation name: ${session.dialogData.reservationName}`); - session.endDialog(); - } -]).set('storage', inMemoryStorage); // Register in-memory storage -``` - -In this example, the default dialog has four functions, each one representing a step in the waterfall. Each step prompts the user for input and sends the results to the next step to be processed. This process continues until the last step executes, thereby confirming the reservation and ending the dialog. - -The following screenshot shows the results of this bot running in the [Bot Framework Emulator](~/bot-service-debug-emulator.md). - -![Manage conversation flow with waterfall](~/media/bot-builder-nodejs-dialog-manage-conversation/waterfall-results.png) - -## Create a conversation with multiple waterfalls - -You can use multiple waterfalls within a conversation to define whatever conversation structure your bot requires. For example, you might use one waterfall to manage the conversation flow and use additional waterfalls to collect information from the user. Each waterfall is encapsulated in a dialog and can be invoked by calling the `session.beginDialog` function. - -The following code sample shows how to use multiple waterfalls in a conversation. The waterfall within the `greetings` dialog manages the flow of the conversation, while the waterfall within the `askName` dialog collects information from the user. - -```javascript -bot.dialog('greetings', [ - function (session) { - session.beginDialog('askName'); - }, - function (session, results) { - session.endDialog('Hello %s!', results.response); - } -]); -bot.dialog('askName', [ - function (session) { - builder.Prompts.text(session, 'Hi! What is your name?'); - }, - function (session, results) { - session.endDialogWithResult(results); - } -]); -``` - -## Advance the waterfall - -A waterfall progresses from step to step in the sequence that the functions are defined in the array. The first function within a waterfall can receive the arguments that are passed to the dialog. Any function within a waterfall can use the `next` function to proceed to the next step without prompting user for input. - -The following code sample shows how to use the `next` function within a dialog that walks the user through the process of providing information for their user profile. In each step of the waterfall, the bot prompts the user for a piece of information (if necessary), and the user's response (if any) is processed by the subsequent step of the waterfall. The final step of the waterfall in the `ensureProfile` dialog ends that dialog and returns the completed profile information to the calling dialog, which then sends a personalized greeting to the user. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -// This bot ensures user's profile is up to date. -var bot = new builder.UniversalBot(connector, [ - function (session) { - session.beginDialog('ensureProfile', session.userData.profile); - }, - function (session, results) { - session.userData.profile = results.response; // Save user profile. - session.send(`Hello ${session.userData.profile.name}! I love ${session.userData.profile.company}!`); - } -]).set('storage', inMemoryStorage); // Register in-memory storage - -bot.dialog('ensureProfile', [ - function (session, args, next) { - session.dialogData.profile = args || {}; // Set the profile or create the object. - if (!session.dialogData.profile.name) { - builder.Prompts.text(session, "What's your name?"); - } else { - next(); // Skip if we already have this info. - } - }, - function (session, results, next) { - if (results.response) { - // Save user's name if we asked for it. - session.dialogData.profile.name = results.response; - } - if (!session.dialogData.profile.company) { - builder.Prompts.text(session, "What company do you work for?"); - } else { - next(); // Skip if we already have this info. - } - }, - function (session, results) { - if (results.response) { - // Save company name if we asked for it. - session.dialogData.profile.company = results.response; - } - session.endDialogWithResult({ response: session.dialogData.profile }); - } -]); -``` - -> [!NOTE] -> This example uses two different data bags to store data: `dialogData` and `userData`. If the bot is distributed across multiple compute nodes, each step of the waterfall could be processed by a different node. For more information about storing bot data, see [Manage state data](bot-builder-nodejs-state.md). - -## End a waterfall - -A dialog that is created using a waterfall must be explicitly ended, otherwise the bot will repeat the waterfall indefinitely. You can end a waterfall by using one of the following methods: - -* `session.endDialog`: Use this method to end the waterfall if there is no data to return to the calling dialog. - -* `session.endDialogWithResult`: Use this method to end the waterfall if there is data to return to the calling dialog. The `response` argument that is returned can be a JSON object or any JavaScript primitive data type. For example: - ```javascript - session.endDialogWithResult({ - response: { name: session.dialogData.name, company: session.dialogData.company } - }); - ``` - -* `session.endConversation`: Use this method to end the waterfall if the end of the waterfall represents the end of the conversation. - -As an alternative to using one of these three methods to end a waterfall, you can attach the `endConversationAction` trigger to the dialog. For example: - -```javascript -bot.dialog('dinnerOrder', [ - //...waterfall steps..., - // Last step - function(session, results){ - if(results.response){ - session.dialogData.room = results.response; - var msg = `Thank you. Your order will be delivered to room #${session.dialogData.room}`; - session.endConversation(msg); - } - } -]) -.endConversationAction( - "endOrderDinner", "Ok. Goodbye.", - { - matches: /^cancel$|^goodbye$/i, - confirmPrompt: "This will cancel your order. Are you sure?" - } -); -``` - -## Next steps - -Using waterfall, you can collect information from the user with *prompts*. Let's dive into how you can prompt user for input. - -> [!div class="nextstepaction"] -> [Prompt user for input](bot-builder-nodejs-dialog-prompt.md) diff --git a/articles/nodejs/bot-builder-nodejs-handle-conversation-events.md b/articles/nodejs/bot-builder-nodejs-handle-conversation-events.md deleted file mode 100644 index 660eb0cfa..000000000 --- a/articles/nodejs/bot-builder-nodejs-handle-conversation-events.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Handle user and conversation events - Bot Service -description: Learn how to handle events such as a user joining a conversation using the Bot Framework SDK for Node.js. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Handle user and conversation events - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -This article demonstrates how your bot can handle events such as a user joining a conversation, adding a bot to a contacts list, or saying **Goodbye** when a bot is being removed from a conversation. - - -## Greet a user on conversation join -The Bot Framework provides the [conversationUpdate][conversationUpdate] event for notifying your bot whenever a member joins or leaves a conversation. A conversation member can be a user or a bot. - -The following code snippet allows the bot to greet new member(s) to a conversation or say **Goodbye** if it is being removed from the conversation. - -[!INCLUDE [conversationUpdate sample Node.js](../includes/snippet-code-node-conversationupdate-1.md)] - -## Acknowledge add to contacts list - -The [contactRelationUpdate][contactRelationUpdate] event notifies your bot that a user has added you to their contacts list. - -[!INCLUDE [contactRelationUpdate sample Node.js](../includes/snippet-code-node-contactrelationupdate-1.md)] - -## Add a first-run dialog - -Since the **conversationUpdate** and the **contactRelationUpdate** event are not supported by all channels, a universal way to greet a user who joins a conversation is to add a first-run dialog. - -In the following example we’ve added a function that triggers the dialog any time we’ve never seen a user before. You can customize the way an action is triggered by providing an [onFindAction][onFindAction] handler for your action. - -[!INCLUDE [first-run sample Node.js](../includes/snippet-code-node-first-run-dialog-1.md)] - -You can also customize what an action does after its been triggered by providing an [onSelectAction][onSelectAction] handler. For trigger actions you can provide an [onInterrupted][onInterrupted] handler to intercept an interruption before it occurs. For more information, see [Handle user actions](bot-builder-nodejs-dialog-actions.md) - -## Additional resources - -* [conversationUpdate][conversationUpdate] -* [contactRelationUpdate][contactRelationUpdate] - -[conversationUpdate]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iconversationupdate.html -[contactRelationUpdate]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.icontactrelationupdate.html - -[onFindAction]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions#onfindaction -[onSelectAction]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions#onselectaction -[onInterrupted]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions#oninterrupted - -[SendTyping]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#sendtyping -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage -[ChatConnector]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html -[session_userData]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#userdata diff --git a/articles/nodejs/bot-builder-nodejs-intercept-messages.md b/articles/nodejs/bot-builder-nodejs-intercept-messages.md deleted file mode 100644 index 65c07292c..000000000 --- a/articles/nodejs/bot-builder-nodejs-intercept-messages.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Intercept messages (v3 JS) - Bot Service -description: Learn how to create logs or other records by intercepting and processing information exchanges using the Bot Framework SDK for Node.js. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/02/2018 -monikerRange: 'azure-bot-service-3.0' ---- -# Intercept messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-middleware.md) -> - [Node.js](../nodejs/bot-builder-nodejs-intercept-messages.md) - -[!INCLUDE [Introduction to message logging](../includes/snippet-message-logging-intro.md)] - -## Example - -The following code sample shows how to intercept messages that are exchanged between user and bot by using the concept of **middleware** in the Bot Framework SDK for Node.js. - -First, configure the handler for incoming messages (`botbuilder`) and for outgoing messages (`send`). - -```javascript -server.post('/api/messages', connector.listen()); -var bot = new builder.UniversalBot(connector); -bot.use({ - botbuilder: function (session, next) { - myMiddleware.logIncomingMessage(session, next); - }, - send: function (event, next) { - myMiddleware.logOutgoingMessage(event, next); - } -}) -``` - -Then, implement each of the handlers to define the action to take for each message that is intercepted. - -```javascript -module.exports = { - logIncomingMessage: function (session, next) { - console.log(session.message.text); - next(); - }, - logOutgoingMessage: function (event, next) { - console.log(event.text); - next(); - } -} -``` - -Now, every inbound message (from user to bot) will trigger `logIncomingMessage`, -and every outbound message (from bot to user) will trigger `logOutgoingMessage`. -In this example, the bot simply prints some information about each message, but you can -update `logIncomingMessage` and `logOutgoingMessage` as necessary to define the actions that you want to take for each message. - -## Sample code - -For a complete sample that shows how to intercept and log messages using the Bot Framework SDK for Node.js, see the Middleware and Logging sample in GitHub. diff --git a/articles/nodejs/bot-builder-nodejs-localization.md b/articles/nodejs/bot-builder-nodejs-localization.md deleted file mode 100644 index cd0138c40..000000000 --- a/articles/nodejs/bot-builder-nodejs-localization.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Support localization - Bot Service -description: Learn how to determine where the user is and enable localization functionality using the Bot Framework SDK for Node.js. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Support localization - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Bot Builder includes a rich localization system for building bots that can communicate with the user in multiple languages. All of your bot's prompts can be localized using JSON files stored in your bot's directory structure. If you’re using a system like LUIS to perform natural language processing, you can configure your [LuisRecognizer][LUISRecognizer] with a separate model for each language your bot supports and the SDK will automatically select the model that matches the user's preferred locale. - -## Determine the locale by prompting the user -The first step to localizing your bot is adding the ability to identify the user's preferred language. The SDK provides a [session.preferredLocale()][preferredLocal] method to both save and retrieve this preference on a per-user basis. The following example is a dialog to prompt the user for their preferred language and then save their choice. - -``` javascript -bot.dialog('/localePicker', [ - function (session) { - // Prompt the user to select their preferred locale - builder.Prompts.choice(session, "What's your preferred language?", 'English|Español|Italiano'); - }, - function (session, results) { - // Update preferred locale - var locale; - switch (results.response.entity) { - case 'English': - locale = 'en'; - break; - case 'Español': - locale = 'es'; - break; - case 'Italiano': - locale = 'it'; - break; - } - session.preferredLocale(locale, function (err) { - if (!err) { - // Locale files loaded - session.endDialog(`Your preferred language is now ${results.response.entity}`); - } else { - // Problem loading the selected locale - session.error(err); - } - }); - } -]); -``` - -## Determine the locale by using analytics -Another way to determine the user's locale is to use a service like the [Text Analytics API](/azure/cognitive-services/cognitive-services-text-analytics-quick-start) to automatically detect the user's language based upon the text of the message they sent. - -The code snippet below illustrates how you can incorporate this service into your own bot. -``` javascript -var request = require('request'); - -bot.use({ - receive: function (event, next) { - if (event.text && !event.textLocale) { - var options = { - method: 'POST', - url: 'https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/languages?numberOfLanguagesToDetect=1', - body: { documents: [{ id: 'message', text: event.text }]}, - json: true, - headers: { - 'Ocp-Apim-Subscription-Key': '' - } - }; - request(options, function (error, response, body) { - if (!error && body) { - if (body.documents && body.documents.length > 0) { - var languages = body.documents[0].detectedLanguages; - if (languages && languages.length > 0) { - event.textLocale = languages[0].iso6391Name; - } - } - } - next(); - }); - } else { - next(); - } - } -}); -``` - -Once you add the above code snippet to your bot, calling [session.preferredLocale()][preferredLocal] will automatically return the detected language. The search order for `preferredLocale()` is as follows: -1. Locale saved by calling `session.preferredLocale()`. This value is stored in `session.userData['BotBuilder.Data.PreferredLocale']`. -2. Detected locale assigned to `session.message.textLocale`. -3. The configured default locale for the bot (e.g.: English (‘en’)). - -You can configure the bot's default locale using its constructor: - -```javascript -var bot = new builder.UniversalBot(connector, { - localizerSettings: { - defaultLocale: "es" - } -}); -``` - -## Localize prompts -The default localization system for the Bot Framework SDK is file-based and allows a bot to support multiple languages using JSON files stored on disk. By default, the localization system will search for the bot's prompts in the **./locale//index.json** file where is a valid [IETF language tag][IEFT] representing the preferred locale for which to find prompts. - -The following screenshot shows the directory structure for a bot that supports three languages: English, Italian, and Spanish. - -![Directory structure for three locales](../media/locale-dir.png) - -The structure of the file is a simple JSON map of message IDs to localized text strings. If the value is an array instead of a string, one prompt from the array is chosen at random when that value is retrieved using [session.localizer.gettext()][GetText]. - -The bot automatically retrieves the localized version of a message if you pass the message ID in a call to [session.send()](http://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#send) instead of language-specific text: - -```javascript -var bot = new builder.UniversalBot(connector, [ - function (session) { - session.send("greeting"); - session.send("instructions"); - session.beginDialog('/localePicker'); - }, - function (session) { - builder.Prompts.text(session, "text_prompt"); - } -]); -``` - -Internally, the SDK calls [`session.preferredLocale()`][preferredLocale] to get the user's preferred locale and then uses that in a call to [`session.localizer.gettext()`][GetText] to map the message ID to its localized text string. There are times where you may need to manually call the localizer. For instance, the enum values passed to [`Prompts.choice()`][promptsChoice] are never automatically localized so you may need to manually retrieve a localized list prior to calling the prompt: - -```javascript -var options = session.localizer.gettext(session.preferredLocale(), "choice_options"); -builder.Prompts.choice(session, "choice_prompt", options); -``` - -The default localizer searches for a message ID across multiple files and if it can’t find an ID (or if no localization files were provided) it will simply return the text of ID, making the use of localization files transparent and optional. Files are searched in the following order: - -1. The **index.json** file under the locale returned by [`session.preferredLocale()`][preferredLocale] is searched. -2. If the locale included an optional subtag like **en-US** then the root tag of **en** is searched. -3. The bot's configured default locale is searched. - -## Use namespaces to customize and localize prompts -The default localizer supports the namespacing of prompts to avoid collisions between message IDs. Your bot can override namespaced prompts to customize or reword the prompts from another namespace. You can leverage this capability to customize the SDK’s built-in messages, letting you either add support for additional languages or to simply reword the SDK's current messages. For instance, you can change the SDK’s default error message by simply adding a file called **BotBuilder.json** to your bot's locale directory and then adding an entry for the `default_error` message ID: - -![BotBuilder.json for locale namespacing](../media/locale-namespacing.png) - - -## Additional resources - -To learn about how to localize a recognizer, see [Recognizing intent](bot-builder-nodejs-recognize-intent-messages.md). - - -[LUIS]: https://www.luis.ai/ -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage -[IntentRecognizerSetOptions]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iintentrecognizersetoptions.html -[LUISRecognizer]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.luisrecognizer -[LUISSample]: https://aka.ms/v3-js-luisSample -[DisambiguationSample]: https://aka.ms/v3-js-onDisambiguateRoute -[preferredLocal]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#preferredlocale -[preferredLocale]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#preferredlocale -[promptsChoice]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.__global.iprompts.html#choice -[GetText]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.ilocalizer.html#gettext -[IEFT]: https://en.wikipedia.org/wiki/IETF_language_tag - diff --git a/articles/nodejs/bot-builder-nodejs-message-create.md b/articles/nodejs/bot-builder-nodejs-message-create.md deleted file mode 100644 index 6c8073033..000000000 --- a/articles/nodejs/bot-builder-nodejs-message-create.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Create messages - Bot Service -description: Learn how create messages with the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 09/7/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Create messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Communication between the bot and the user is through messages. Your bot will send message activities to communicate information to users, and likewise, will also receive message activities from users. Some messages may simply consist of plain text, while others may contain richer content such as text to be spoken, suggested actions, media attachments, rich cards, and channel-specific data. - -This article describes some of the commonly-used message methods you can use to enhance your user experience. - -## Default message handler - -The Bot Framework SDK for Node.js comes with a default message handler built into the [`session`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html) object. This message handler allows you to send and receive text messages between the bot and the user. - -### Send a text message - -Sending a text message using the default message handler is simple, just call `session.send` and pass in a **string**. - -This sample shows how you can send a text message to greet the user. -```javascript -session.send("Good morning."); -``` - -This sample shows how you can send a text message using JavaScript string template. -```javascript -var msg = `You ordered: ${order.Description} for a total of $${order.Price}.`; -session.send(msg); //msg: "You ordered: Potato Salad for a total of $5.99." -``` - -### Receive a text message - -When a user sends the bot a message, the bot receives the message through the `session.message` property. - -This sample shows how to access the user's message. -```javascript -var userMessage = session.message.text; -``` - -## Customizing a message - -To have more control over the text formatting of your messages, you can create a custom [`message`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html) object and set the properties necessary before sending it to the user. - -This sample shows how to create a custom `message` object and set the `text`, `textFormat`, and `textLocale` properties. - -```javascript -var customMessage = new builder.Message(session) - .text("Hello!") - .textFormat("plain") - .textLocale("en-us"); -session.send(customMessage); -``` - -In cases where you do not have the `session` object in scope, you can use `bot.send` method to send a formatted message to the user. - -The `textFormat` property of a message can be used to specify the format of the text. The `textFormat` property can be set to **plain**, **markdown**, or **xml**. The default value for `textFormat` is **markdown**. - -## Message property - -The [`Message`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html) object has an internal **data** property that it uses to manage the message being sent. Other properties you set are through the different methods this object expose to you. - -## Message methods - -Message properties are set and retrieved through the object's methods. The table below provide a list of the methods you can call to set/get the different **Message** properties. - -| Method | Description | -| ---- | ---- | -| [`addAttachment(attachment:AttachmentType)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#addattachment) | Adds an attachment to a message| -| [`addEntity(obj:Object)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#addentity) | Adds an entity to the message. | -| [`address(adr:IAddress)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#address) | Address routing information for the message. To send user a proactive message, save the message's address in the userData bag. | -| [`attachmentLayout(style:string)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#attachmentlayout) | Hint for how clients should layout multiple attachments. The default value is 'list'. | -| [`attachments(list:AttachmentType)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#attachments) | A list of cards or images to send to the user. | -| [`compose(prompts:string[], ...args:any[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#compose) | Composes a complex and randomized reply to the user. | -| [`entities(list:Object[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#entities) | Structured objects passed to the bot or user. | -| [`inputHint(hint:string)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#inputhint) | Hint sent to user letting them know if the bot is expecting further input or not. The built-in prompts will automatically populate this value for outgoing messages. | -| [`localTimeStamp((optional)time:string)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#localtimestamp) | Local time when message was sent (set by client or bot, Ex: 2016-09-23T13:07:49.4714686-07:00.) | -| [`originalEvent(event:any)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#originalevent) | Message in original/native format of the channel for incoming messages. | -| [`sourceEvent(map:ISourceEventMap)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#sourceevent) | For outgoing messages can be used to pass source specific event data like custom attachments. | -| [`speak(ssml:TextType, ...args:any[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#speak) | Sets the speak field of the message as *Speech Synthesis Markup Language (SSML)*. This will be spoken to the user on supported devices. | -| [`suggestedActions(suggestions:ISuggestedActions `|` IIsSuggestedActions)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#suggestedactions) | Optional suggested actions to send to the user. Suggested actions will be displayed only on the channels that support suggested actions. | -| [`summary(text:TextType, ...argus:any[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#summary) | Text to be displayed as fall-back and as short description of the message content in (e.g.: List of recent conversations.) | -| [`text(text:TextType, ...args:any[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#text) | Sets the message text. | -| [`textFormat(style:string)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#textformat) | Set the text format. Default format is **markdown**. | -| [`textLocale(locale:string)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#textlocale) | Set the target language of the message. | -| [`toMessage()`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#tomessage) | Gets the JSON for the message. | -| [`composePrompt(session:Session, prompts:string[], args?:any[])`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#composeprompt-1) | Combines an array of prompts into a single localized prompt and then optionally fills the prompts template slots with the passed in arguments. | -| [`randomPrompt(prompts:TextType)`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message.html#randomprompt) | Gets a random prompt from the array of **prompts* that is passed in. | - -## Next step - -> [!div class="nextstepaction"] -> [Send and receive attachments](bot-builder-nodejs-send-receive-attachments.md) - diff --git a/articles/nodejs/bot-builder-nodejs-overview.md b/articles/nodejs/bot-builder-nodejs-overview.md deleted file mode 100644 index 95d7a6e65..000000000 --- a/articles/nodejs/bot-builder-nodejs-overview.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Bot Framework SDK for Node.js - Bot Service -description: Explore the Bot Framework SDK for Node.js, a powerful, easy-to-use bot building framework. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Bot Framework SDK for Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-overview.md) -> - [Node.js](../nodejs/bot-builder-nodejs-overview.md) -> - [REST](../rest-api/bot-framework-rest-overview.md) - -Bot Framework SDK for Node.js is a powerful, easy-to-use framework that provides a familiar way for Node.js developers to write bots. -You can use it to build a wide variety of conversational user interfaces, from simple prompts to free-form conversations. - -The conversational logic for your bot is hosted as a web service. The Bot Framework SDK uses restify, a popular framework for building web services, to create the bot's web server. -The SDK is also compatible with Express and the use of other web app frameworks is possible with some adaption. - -Using the SDK, you can take advantage of the following SDK features: - -- Powerful system for building dialogs to encapsulate conversational logic. -- Built-in prompts for simple things such as Yes/No, strings, numbers, and enumerations, as well as support for messages containing images and attachments, and rich cards containing buttons. -- Built-in support for powerful AI frameworks such as LUIS. -- Built-in recognizers and event handlers that guide the user through the conversation, providing help, navigation, clarification, and confirmation as needed. - -## Get started - -If you are new to writing bots, [create your first bot with Node.js](bot-builder-nodejs-quickstart.md) with step-by-step instructions to help you set up your project, install the SDK, and run your first bot. - -If you are new to the Bot Framework SDK for Node.js, you can start with key concepts that help you understand the major components of the Bot Framework SDK, see [Key concepts](bot-builder-nodejs-concepts.md). - -To ensure your bot addresses the top user scenarios, review the [design principles](../bot-service-design-principles.md) and [explore patterns](../bot-service-design-pattern-task-automation.md) for guidance. - -## Get samples - -The [Bot Framework SDK for Node.js samples](bot-builder-nodejs-samples.md) demonstrate task-focused bots that show how to take advantage of features in the Bot Framework SDK for Node.js. You can use the samples to help you quickly get started with building great bots with rich capabilities. - -## Next steps -> [!div class="nextstepaction"] -> [Key concepts](bot-builder-nodejs-concepts.md) - -## Additional resources - -The following task-focused how-to guides demonstrate various features of the Bot Framework SDK for Node.js. - -* [Respond to messages](bot-builder-nodejs-use-default-message-handler.md) -* [Handle user actions](bot-builder-nodejs-dialog-actions.md) -* [Recognize user intent](bot-builder-nodejs-recognize-intent-messages.md) -* [Send a rich card](bot-builder-nodejs-send-rich-cards.md) -* [Send attachments](bot-builder-nodejs-send-receive-attachments.md) -* [Saving user data](bot-builder-nodejs-save-user-data.md) - - -If you encounter problems or have suggestions regarding the Bot Framework SDK for Node.js, -see [Support](../bot-service-resources-links-help.md) for a list of available resources. - - -[DesignGuide]: ../bot-service-design-principles.md -[DesignPatterns]: ../bot-service-design-pattern-task-automation.md -[HowTo]: bot-builder-nodejs-use-default-message-handler.md diff --git a/articles/nodejs/bot-builder-nodejs-proactive-messages.md b/articles/nodejs/bot-builder-nodejs-proactive-messages.md deleted file mode 100644 index a8366a92f..000000000 --- a/articles/nodejs/bot-builder-nodejs-proactive-messages.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Send proactive messages (v3 JS) - Bot Service -description: Learn how to interrupt the current conversation flow with a proactive message using the Bot Framework SDK for Node.js -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- -# Send proactive messages -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-proactive-messages.md) -> - [Node.js](../nodejs/bot-builder-nodejs-proactive-messages.md) - -[!INCLUDE [Introduction to proactive messages - part 1](../includes/snippet-proactive-messages-intro-1.md)] - -## Types of proactive messages - -[!INCLUDE [Introduction to proactive messages - part 2](../includes/snippet-proactive-messages-intro-2.md)] - -## Send an ad hoc proactive message - -The following code samples show how to send an ad hoc proactive message by using the Bot Framework SDK for Node.js. - -To be able to send an ad hoc message to a user, the bot must first collect and save information about the user from the current conversation. -The **address** property of the message includes all of the information that the bot will need to send an ad hoc message to the user later. - -```javascript -bot.dialog('adhocDialog', function(session, args) { - var savedAddress = session.message.address; - - // (Save this information somewhere that it can be accessed later, such as in a database, or session.userData) - session.userData.savedAddress = savedAddress; - - var message = 'Hello user, good to meet you! I now know your address and can send you notifications in the future.'; - session.send(message); -}) -``` - -> [!NOTE] -> The bot can store the user data in any manner as long as the bot can access it later. - -After the bot has collected information about the user, it can send an ad hoc proactive message to the user at any time. -To do so, it simply retrieves the user data that it stored previously, constructs the message, and sends it. - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); - -var bot = new builder.UniversalBot(connector) - .set('storage', inMemoryStorage); // Register in-memory storage - -function sendProactiveMessage(address) { - var msg = new builder.Message().address(address); - msg.text('Hello, this is a notification'); - msg.textLocale('en-US'); - bot.send(msg); -} -``` - -> [!TIP] -> An ad hoc proactive message can be initiated like from -> asynchronous triggers such as http requests, timers, queues or from anywhere else that the developer chooses. - -## Send a dialog-based proactive message - -The following code samples show how to send a dialog-based proactive message by using the Bot Framework SDK for Node.js. You can find the complete working example in the [startNewDialog](https://aka.ms/js-startnewdialog-sample-v3) folder. - -To be able to send a dialog-based message to a user, the bot must first collect (and save) information from the current conversation. -The `session.message.address` object includes all of the information that the bot will need to send a dialog-based proactive message to the user. - -```javascript -// proactiveDialog dialog -bot.dialog('proactiveDialog', function (session, args) { - - savedAddress = session.message.address; - - var message = 'Hey there, I\'m going to interrupt our conversation and start a survey in five seconds...'; - session.send(message); - - message = `You can also make me send a message by accessing: http://localhost:${server.address().port}/api/CustomWebApi`; - session.send(message); - - setTimeout(() => { - startProactiveDialog(savedAddress); - }, 5000); -}); -``` - -When it is time to send the message, the bot creates a new dialog and adds it to the top of the dialog stack. The new dialog takes control of the conversation, delivers the proactive message, closes, and then returns control to the previous dialog in the stack. - -```javascript -// initiate a dialog proactively -function startProactiveDialog(address) { - bot.beginDialog(address, "*:survey"); -} -``` - -> [!NOTE] -> The code sample above requires a custom file, **botadapter.js**, which you can [download from GitHub](https://aka.ms/js-botadaptor-file-v3). - -The survey dialog controls the conversation until it finishes. -Then, it closes (by calling `session.endDialog()`), thereby returning control back to the previous dialog. - - -```javascript -// handle the proactive initiated dialog -bot.dialog('survey', function (session, args, next) { - if (session.message.text === "done") { - session.send("Great, back to the original conversation"); - session.endDialog(); - } else { - session.send('Hello, I\'m the survey dialog. I\'m interrupting your conversation to ask you a question. Type "done" to resume'); - } -}); -``` - -## Sample code - -For a complete sample that shows how to send proactive messages using the Bot Framework SDK for Node.js, see the Proactive Messages sample in GitHub. -Within the Proactive Messages sample, simpleSendMessage shows how to send an ad-hoc proactive message and startNewDialog shows how to send a dialog-based proactive message. - -## Additional resources - -- [Designing conversation flow](../bot-service-design-conversation-flow.md) diff --git a/articles/nodejs/bot-builder-nodejs-recognize-intent-luis.md b/articles/nodejs/bot-builder-nodejs-recognize-intent-luis.md deleted file mode 100644 index a9e3c7cc3..000000000 --- a/articles/nodejs/bot-builder-nodejs-recognize-intent-luis.md +++ /dev/null @@ -1,606 +0,0 @@ ---- -title: Recognize intents and entities with LUIS (v3 JS) - Bot Service -description: Integrate a bot with LUIS to detect the user's intent and respond appropriately by triggering dialogs using the Bot Framework SDK for Node.js. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 03/28/2018 -monikerRange: 'azure-bot-service-3.0' - ---- - -# Recognize intents and entities with LUIS - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -This article uses the example of a bot for taking notes, to demonstrate how Language Understanding ([LUIS][LUIS]) helps your bot respond appropriately to natural language input. A bot detects what a user wants to do by identifying their **intent**. This intent is determined from spoken or textual input, or **utterances**. The intent maps utterances to actions that the bot takes, such as invoking a dialog. A bot may also need to extract **entities**, which are important words in utterances. Sometimes entities are required to fulfill an intent. In the example of a note-taking bot, the `Notes.Title` entity identifies the title of each note. - -## Create a Language Understanding bot with Bot Service - -1. In the [Azure portal](https://portal.azure.com), select **Create new resource** in the menu blade and click **See all**. - - ![Create new resource](../media/bot-builder-nodejs-use-luis/bot-service-creation.png) - -2. In the search box, search for **Web App Bot**. - - ![Create new resource](../media/bot-builder-nodejs-use-luis/bot-service-selection.png) - -3. In the **Bot Service** blade, provide the required information, and click **Create**. This creates and deploys the bot service and LUIS app to Azure. - * Set **App name** to your bot’s name. The name is used as the subdomain when your bot is deployed to the cloud (for example, mynotesbot.azurewebsites.net). This name is also used as the name of the LUIS app associated with your bot. Copy it to use later, to find the LUIS app associated with the bot. - * Select the subscription, [resource group](/azure/azure-resource-manager/resource-group-overview), App service plan, and [location](https://azure.microsoft.com/regions/). - * Select the **Language understanding (Node.js)** template for the **Bot template** field. - - ![Bot Service blade](../media/bot-builder-nodejs-use-luis/bot-service-setting-callout-template.png) - - * Check the box to confirm to the terms of service. - -4. Confirm that the bot service has been deployed. - * Click Notifications (the bell icon that is located along the top edge of the Azure portal). The notification will change from **Deployment started** to **Deployment succeeded**. - * After the notification changes to **Deployment succeeded**, click **Go to resource** on that notification. - -## Try the bot - -Confirm that the bot has been deployed by checking the **Notifications**. The notifications will change from **Deployment in progress...** to **Deployment succeeded**. Click **Go to resource** button to open the bot's resources blade. - -Once the bot is registered, click **Test in Web Chat** to open the Web Chat pane. Type "hello" in Web Chat. - - ![Test the bot in Web Chat](../media/bot-builder-nodejs-use-luis/bot-service-web-chat.png) - -The bot responds by saying "You have reached Greeting. You said: hello". This confirms that the bot has received your message and passed it to a default LUIS app that it created. This default LUIS app detected a Greeting intent. - -## Modify the LUIS app - -Log in to [https://www.luis.ai](https://www.luis.ai) using the same account you use to log in to Azure. Click on **My apps**. In the list of apps, find the app that begins with the name specified in **App name** in the **Bot Service** blade when you created the Bot Service. - -The LUIS app starts with 4 intents: Cancel: Greeting, Help, and None. - -The following steps add the Note.Create, Note.ReadAloud, and Note.Delete intents: - -1. Click on **Prebuit Domains** in the lower left of the page. Find the **Note** domain and click **Add domain**. - -2. This tutorial doesn't use all of the intents included in the **Note** prebuilt domain. In the **Intents** page, click on each of the following intent names and then click the **Delete Intent** button. - * Note.ShowNext - * Note.DeleteNoteItem - * Note.Confirm - * Note.Clear - * Note.CheckOffItem - * Note.AddToNote - - The only intents that should remain in the LUIS app are the following: - * Note.ReadAloud - * Note.Create - * Note.Delete - * None - * Help - * Greeting - * Cancel - - ![intents shown in LUIS app](../media/bot-builder-nodejs-use-luis/luis-intent-list.png) - - -3. Click the **Train** button in the upper right to train your app. -4. Click **PUBLISH** in the top navigation bar to open the **Publish** page. Click the **Publish to production slot** button. After successful publish, a LUIS app is deployed to the URL displayed in the **Endpoint** column in the **Publish App** page, in the row that starts with the Resource Name Starter_Key. The URL has a format similar to this example: `https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?subscription-key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&timezoneOffset=0&verbose=true&q=`. The app ID and subscription key in this URL are the same as LuisAppId and LuisAPIKey in **App Service Settings > ApplicationSettings > App settings** - - -## Modify the bot code - -Click **Build** and then click **Open online code editor**. - - ![Open online code editor](../media/bot-builder-nodejs-use-luis/bot-service-build.png) - -In the code editor, open `app.js`. It contains the following code: - -```javascript -var restify = require('restify'); -var builder = require('botbuilder'); -var botbuilder_azure = require("botbuilder-azure"); - -// 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, - 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 -* ---------------------------------------------------------------------------------------- */ - -var tableName = 'botdata'; -var azureTableClient = new botbuilder_azure.AzureTableClient(tableName, process.env['AzureWebJobsStorage']); -var tableStorage = new botbuilder_azure.AzureBotStorage({ gzipData: false }, azureTableClient); - -// Create your bot with a function to receive messages from the user -// This default message handler is invoked if the user's utterance doesn't -// match any intents handled by other dialogs. -var bot = new builder.UniversalBot(connector, function (session, args) { - session.send('You reached the default message handler. You said \'%s\'.', session.message.text); -}); - -bot.set('storage', tableStorage); - -// Make sure you add code to validate these fields -var luisAppId = process.env.LuisAppId; -var luisAPIKey = process.env.LuisAPIKey; -var luisAPIHostName = process.env.LuisAPIHostName || 'westus.api.cognitive.microsoft.com'; - -const LuisModelUrl = 'https://' + luisAPIHostName + '/luis/v2.0/apps/' + luisAppId + '?subscription-key=' + luisAPIKey; - -// Create a recognizer that gets intents from LUIS, and add it to the bot -var recognizer = new builder.LuisRecognizer(LuisModelUrl); -bot.recognizer(recognizer); - -// Add a dialog for each intent that the LUIS app recognizes. -// See https://docs.microsoft.com/bot-framework/nodejs/bot-builder-nodejs-recognize-intent-luis -bot.dialog('GreetingDialog', - (session) => { - session.send('You reached the Greeting intent. You said \'%s\'.', session.message.text); - session.endDialog(); - } -).triggerAction({ - matches: 'Greeting' -}) - -bot.dialog('HelpDialog', - (session) => { - session.send('You reached the Help intent. You said \'%s\'.', session.message.text); - session.endDialog(); - } -).triggerAction({ - matches: 'Help' -}) - -bot.dialog('CancelDialog', - (session) => { - session.send('You reached the Cancel intent. You said \'%s\'.', session.message.text); - session.endDialog(); - } -).triggerAction({ - matches: 'Cancel' -}) - -``` - - -> [!TIP] -> You can also find the sample code described in this article in the [Notes bot sample][NotesSample]. - - - -## Edit the default message handler -The bot has a default message handler. Edit it to match the following: -```javascript -// Create your bot with a function to receive messages from the user. -// This default message handler is invoked if the user's utterance doesn't -// match any intents handled by other dialogs. -var bot = new builder.UniversalBot(connector, function (session, args) { - session.send("Hi... I'm the note bot sample. I can create new notes, read saved notes to you and delete notes."); - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing userData.notes in default message handler"); - } -}); -``` - -## Handle the Note.Create intent - -Copy the following code and paste it at the end of app.js: - -```javascript -// CreateNote dialog -bot.dialog('CreateNote', [ - function (session, args, next) { - // Resolve and store any Note.Title entity passed from LUIS. - var intent = args.intent; - var title = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - - var note = session.dialogData.note = { - title: title ? title.entity : null, - }; - - // Prompt for title - if (!note.title) { - builder.Prompts.text(session, 'What would you like to call your note?'); - } else { - next(); - } - }, - function (session, results, next) { - var note = session.dialogData.note; - if (results.response) { - note.title = results.response; - } - - // Prompt for the text of the note - if (!note.text) { - builder.Prompts.text(session, 'What would you like to say in your note?'); - } else { - next(); - } - }, - function (session, results) { - var note = session.dialogData.note; - if (results.response) { - note.text = results.response; - } - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing session.userData.notes in CreateNote dialog"); - } - // Save notes in the notes object - session.userData.notes[note.title] = note; - - // Send confirmation to user - session.endDialog('Creating note named "%s" with text "%s"', - note.title, note.text); - } -]).triggerAction({ - matches: 'Note.Create', - confirmPrompt: "This will cancel the creation of the note you started. Are you sure?" -}).cancelAction('cancelCreateNote', "Note canceled.", { - matches: /^(cancel|nevermind)/i, - confirmPrompt: "Are you sure?" -}); -``` - -Any entities in the utterance are passed to the dialog using the `args` parameter. The first step of the [waterfall][waterfall] calls [EntityRecognizer.findEntity][EntityRecognizer_findEntity] to get the title of the note from any `Note.Title` entities in the LUIS response. If the LUIS app didn't detect a `Note.Title` entity, the bot prompts the user for the name of the note. The second step of the waterfall prompts for the text to include in the note. Once the bot has the text of the note, the third step uses [session.userData][session_userData] to save the note in a `notes` object, using the title as the key. For more information on `session.UserData` see [Manage state data](./bot-builder-nodejs-state.md). - - - -## Handle the Note.Delete intent -Just as for the `Note.Create` intent, the bot examines the `args` parameter for a title. If no title is detected, the bot prompts the user. The title is used to look up the note to delete from `session.userData.notes`. - - - -Copy the following code and paste it at the end of app.js: -```javascript -// Delete note dialog -bot.dialog('DeleteNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify that the title is in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to delete?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to delete."); - } - }, - function (session, results) { - delete session.userData.notes[results.response.entity]; - session.endDialog("Deleted the '%s' note.", results.response.entity); - } -]).triggerAction({ - matches: 'Note.Delete' -}).cancelAction('cancelDeleteNote', "Ok - canceled note deletion.", { - matches: /^(cancel|nevermind)/i -}); -``` - -The code that handles `Note.Delete` uses the `noteCount` function to determine whether the `notes` object contains notes. - -Paste the `noteCount` helper function at the end of `app.js`. - -[!code-js[Add a helper function that returns the number of notes (JavaScript)](../includes/code/node-basicNote.js#CountNotesHelper)] - -## Handle the Note.ReadAloud intent - -Copy the following code and paste it in `app.js` after the handler for `Note.Delete`: - -```javascript -// Read note dialog -bot.dialog('ReadNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify it's in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to read?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to read."); - } - }, - function (session, results) { - session.endDialog("Here's the '%s' note: '%s'.", results.response.entity, session.userData.notes[results.response.entity].text); - } -]).triggerAction({ - matches: 'Note.ReadAloud' -}).cancelAction('cancelReadNote', "Ok.", { - matches: /^(cancel|nevermind)/i -}); - -``` - -The `session.userData.notes` object is passed as the third argument to `builder.Prompts.choice`, so that the prompt displays a list of notes to the user. - -Now that you've added handlers for the new intents, the full code for `app.js` contains the following: - -```javascript -var restify = require('restify'); -var builder = require('botbuilder'); -var botbuilder_azure = require("botbuilder-azure"); - -// 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, - 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 -* ---------------------------------------------------------------------------------------- */ - -var tableName = 'botdata'; -var azureTableClient = new botbuilder_azure.AzureTableClient(tableName, process.env['AzureWebJobsStorage']); -var tableStorage = new botbuilder_azure.AzureBotStorage({ gzipData: false }, azureTableClient); - -// Create your bot with a function to receive messages from the user. -// This default message handler is invoked if the user's utterance doesn't -// match any intents handled by other dialogs. -var bot = new builder.UniversalBot(connector, function (session, args) { - session.send("Hi... I'm the note bot sample. I can create new notes, read saved notes to you and delete notes."); - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing userData.notes in default message handler"); - } -}); - -bot.set('storage', tableStorage); - -// Make sure you add code to validate these fields -var luisAppId = process.env.LuisAppId; -var luisAPIKey = process.env.LuisAPIKey; -var luisAPIHostName = process.env.LuisAPIHostName || 'westus.api.cognitive.microsoft.com'; - -const LuisModelUrl = 'https://' + luisAPIHostName + '/luis/v2.0/apps/' + luisAppId + '?subscription-key=' + luisAPIKey; - -// Create a recognizer that gets intents from LUIS, and add it to the bot -var recognizer = new builder.LuisRecognizer(LuisModelUrl); -bot.recognizer(recognizer); - -// CreateNote dialog -bot.dialog('CreateNote', [ - function (session, args, next) { - // Resolve and store any Note.Title entity passed from LUIS. - var intent = args.intent; - var title = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - - var note = session.dialogData.note = { - title: title ? title.entity : null, - }; - - // Prompt for title - if (!note.title) { - builder.Prompts.text(session, 'What would you like to call your note?'); - } else { - next(); - } - }, - function (session, results, next) { - var note = session.dialogData.note; - if (results.response) { - note.title = results.response; - } - - // Prompt for the text of the note - if (!note.text) { - builder.Prompts.text(session, 'What would you like to say in your note?'); - } else { - next(); - } - }, - function (session, results) { - var note = session.dialogData.note; - if (results.response) { - note.text = results.response; - } - - // If the object for storing notes in session.userData doesn't exist yet, initialize it - if (!session.userData.notes) { - session.userData.notes = {}; - console.log("initializing session.userData.notes in CreateNote dialog"); - } - // Save notes in the notes object - session.userData.notes[note.title] = note; - - // Send confirmation to user - session.endDialog('Creating note named "%s" with text "%s"', - note.title, note.text); - } -]).triggerAction({ - matches: 'Note.Create', - confirmPrompt: "This will cancel the creation of the note you started. Are you sure?" -}).cancelAction('cancelCreateNote', "Note canceled.", { - matches: /^(cancel|nevermind)/i, - confirmPrompt: "Are you sure?" -}); - -// Delete note dialog -bot.dialog('DeleteNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify that the title is in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to delete?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to delete."); - } - }, - function (session, results) { - delete session.userData.notes[results.response.entity]; - session.endDialog("Deleted the '%s' note.", results.response.entity); - } -]).triggerAction({ - matches: 'Note.Delete' -}).cancelAction('cancelDeleteNote', "Ok - canceled note deletion.", { - matches: /^(cancel|nevermind)/i -}); - - -// Read note dialog -bot.dialog('ReadNote', [ - function (session, args, next) { - if (noteCount(session.userData.notes) > 0) { - - // Resolve and store any Note.Title entity passed from LUIS. - var title; - var intent = args.intent; - var entity = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title'); - if (entity) { - // Verify it's in our set of notes. - title = builder.EntityRecognizer.findBestMatch(session.userData.notes, entity.entity); - } - - // Prompt for note name - if (!title) { - builder.Prompts.choice(session, 'Which note would you like to read?', session.userData.notes); - } else { - next({ response: title }); - } - } else { - session.endDialog("No notes to read."); - } - }, - function (session, results) { - session.endDialog("Here's the '%s' note: '%s'.", results.response.entity, session.userData.notes[results.response.entity].text); - } -]).triggerAction({ - matches: 'Note.ReadAloud' -}).cancelAction('cancelReadNote', "Ok.", { - matches: /^(cancel|nevermind)/i -}); - - -// Helper function to count the number of notes stored in session.userData.notes -function noteCount(notes) { - - var i = 0; - for (var name in notes) { - i++; - } - return i; -} -``` - -## Test the bot - -In the Azure Portal, click on **Test in Web Chat** to test the bot. Try type messages like "Create a note", "read my notes", and "delete notes" to invoke the intents that you added to it. - ![Test notes bot in Web Chat](../media/bot-builder-nodejs-use-luis/bot-service-test-notebot.png) - -> [!TIP] -> If you find that your bot doesn't always recognize the correct intent or entities, improve your LUIS app's performance by giving it more example utterances to train it. You can retrain your LUIS app without any modification to your bot's code. See [Add example utterances](/azure/cognitive-services/LUIS/add-example-utterances) and [train and test your LUIS app](/azure/cognitive-services/LUIS/train-test). - - -## Next steps - -From trying the bot, you can see that the recognizer can trigger interruption of the currently active dialog. Allowing and handling interruptions is a flexible design that accounts for what users really do. Learn more about the various actions you can associate with a recognized intent. - -> [!div class="nextstepaction"] -> [Handle user actions](bot-builder-nodejs-dialog-actions.md) - - -[LUIS]: https://www.luis.ai/ - -[intentDialog]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.intentdialog.html - -[intentDialog_matches]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.intentdialog.html#matches - -[NotesSample]: https://github.com/Microsoft/BotFramework-Samples/tree/master/docs-samples/Node/basics-naturalLanguage - -[triggerAction]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html#triggeraction - -[confirmPrompt]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions.html#confirmprompt - -[waterfall]: bot-builder-nodejs-dialog-manage-conversation-flow.md#manage-conversation-flow-with-a-waterfall - -[session_userData]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#userdata - -[EntityRecognizer_findEntity]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.entityrecognizer.html#findentity - -[matches]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.itriggeractionoptions.html#matches - -[LUISAzureDocs]: /azure/cognitive-services/LUIS/Home - -[Dialog]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.dialog.html - -[IntentRecognizerSetOptions]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iintentrecognizersetoptions.html - -[LuisRecognizer]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.luisrecognizer - -[LUISConcepts]: https://docs.botframework.com/node/builder/guides/understanding-natural-language/ - -[DisambiguationSample]: https://aka.ms/v3-js-onDisambiguateRoute - -[IDisambiguateRouteHandler]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.idisambiguateroutehandler.html - -[RegExpRecognizer]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.regexprecognizer.html - -[AlarmBot]: https://aka.ms/v3-js-luisSample - -[UniversalBot]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.universalbot.html diff --git a/articles/nodejs/bot-builder-nodejs-recognize-intent-messages.md b/articles/nodejs/bot-builder-nodejs-recognize-intent-messages.md deleted file mode 100644 index c6e1270d9..000000000 --- a/articles/nodejs/bot-builder-nodejs-recognize-intent-messages.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Recognize intent from message content - Bot Service -description: Learn how to recognize the user's intent by using regular expressions or checking the message content. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Recognize user intent from message content - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -When your bot receives a message from a user, your bot can use a **recognizer** to examine the message and determine intent. The intent provides a mapping from messages to dialogs to invoke. This article explains how to recognize intent using regular expressions or by inspecting the content of a message. For example, a bot can use regular expressions to check if a message contains the word "help" and invoke a help dialog. A bot can also check the properties of the user message, for example, to see if the user sent an image instead of text and invoke an image processing dialog. - -> [!NOTE] -> For information on recognizing intent using LUIS, see [Recognize intents and entities with LUIS](bot-builder-nodejs-recognize-intent-luis.md) - -## Use the built-in regular expression recognizer - -Use [RegExpRecognizer][RegExpRecognizer] to detect the user's intent using a regular expression. You can pass multiple expressions to the recognizer to support multiple languages. - -> [!TIP] -> See [Support localization](bot-builder-nodejs-localization.md) for more information on localizing your bot. - -The following code creates a regular expression recognizer named `CancelIntent` and adds it to your bot. The recognizer in this example provides regular expressions for both the `en_us` and `ja_jp` locales. - -[!code-js[Add a regular expression recognizer (JavaScript)](../includes/code/node-regex-recognizer.js#addRegexRecognizer)] - -Once the recognizer is added to your bot, attach a [triggerAction][triggerAction] to the dialog that you want the bot to invoke when the recognizer detects the intent. Use the `matches` option to specify the intent name, as shown in the following code: - -[!code-js[Map the CancelIntent recognizer to a cancel dialog (JavaScript)](../includes/code/node-regex-recognizer.js#bindCancelDialogToRegexRecognizer)] - -Intent recognizers are global, which means that the recognizer will run for every message received from the user. If a recognizer detects an intent that is bound to a dialog using a `triggerAction`, it can trigger interruption of the currently active dialog. Allowing and handling interruptions is a flexible design that accounts for what users really do. - -> [!TIP] -> To learn how a `triggerAction` works with dialogs, see [Managing conversation flow](bot-builder-nodejs-manage-conversation-flow.md). To learn more about the various actions you can associate with a recognized intent, [Handle user actions](bot-builder-nodejs-dialog-actions.md). - -## Register a custom intent recognizer - -You can also implement a custom recognizer. This example adds a simple recognizer that looks for the user to say 'help' or 'goodbye' but you could easily add a recognizer that does more complex processing, such as one that recognizes when the user sends an image. - -[!code-js[Add a custom recognizer (JavaScript)](../includes/code/node-howto-recognize-intent.js#addCustomRecognizer)] - -Once you've registered a recognizer, you can associate the recognizer with an action using a `matches` clause. - -[!code-js[Bind intents to actions (JavaScript)](../includes/code/node-howto-recognize-intent.js#bindIntentsToActions)] - -## Disambiguate between multiple intents - -Your bot can register more than one recognizer. Notice that the custom recognizer example involves assigning a numerical score to each intent. This is done since your bot may have more than one recognizer, and the Bot Framework SDK provides built-in logic to disambiguate between intents returned by multiple recognizers. The score assigned to an intent is typically between 0.0 and 1.0, but a custom recognizer may define an intent greater than 1.1 to ensure that that intent will always be chosen by the Bot Framework SDK disambiguation logic. - -By default, recognizers run in parallel, but you can set recognizeOrder in [IIntentRecognizerSetOptions][IIntentRecognizerSetOptions] so the process quits as soon as your bot finds one that gives a score of 1.0. - -The Bot Framework SDK includes a [sample][DisambiguationSample] that demonstrates how to provide custom disambiguation logic in your bot by implementing [IDisambiguateRouteHandler][IDisambiguateRouteHandler]. - -## Next steps - -The logic for using regular expressions and inspecting message contents can become complex, especially if your bot's conversational flow is open-ended. To help your bot handle a wider variety of textual and spoken input from users, you can use an intent recognition service like [LUIS][LUIS] to add natural language understanding to your bot. - -> [!div class="nextstepaction"] -> [Recognize intents and entities with LUIS](bot-builder-nodejs-recognize-intent-luis.md) - -[LUIS]: https://www.luis.ai/ - -[IDisambiguateRouteHandler]: https://docs.microsoft.com/javascript/api/botbuilder/idisambiguateroutehandler?view=botbuilder-ts-3.0 -[IIntentRecognizerSetOptions]: https://docs.microsoft.com/javascript/api/botbuilder/iintentrecognizersetoptions?view=botbuilder-ts-3.0 -[RegExpRecognizer]: https://docs.microsoft.com/javascript/api/botbuilder/regexprecognizer?view=botbuilder-ts-3.0 -[triggerAction]: https://docs.microsoft.com/javascript/api/botbuilder/dialog?view=botbuilder-ts-3.0#triggeraction-itriggeractionoptions- - -[DisambiguationSample]: https://aka.ms/v3-js-onDisambiguateRoute diff --git a/articles/nodejs/bot-builder-nodejs-request-payment.md b/articles/nodejs/bot-builder-nodejs-request-payment.md deleted file mode 100644 index beb376c95..000000000 --- a/articles/nodejs/bot-builder-nodejs-request-payment.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Request payment (v3 JS) - Bot Service -description: Learn how to send a payment request using the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Request payment - -> [!NOTE] -> The Payment service referred to in this article will be deprecated on December 1, 2019. - - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-request-payment.md) -> - [Node.js](../nodejs/bot-builder-nodejs-request-payment.md) - -If your bot enables users to purchase items, it can request payment by including -a special type of button within a [rich card](bot-builder-nodejs-send-rich-cards.md). -This article describes how to send a payment request using the Bot Framework SDK for Node.js. - -## Prerequisites - -Before you can send a payment request using the Bot Framework SDK for Node.js, you must complete these prerequisite tasks. - -### Register and configure your bot - -Update your bot's environment variables for `MicrosoftAppId` and `MicrosoftAppPassword` to the app ID and password values that were generated for your bot during the [registration](~/bot-service-quickstart-registration.md) process. - -> [!NOTE] -> To find your bot's **AppID** and **AppPassword**, see [MicrosoftAppID and MicrosoftAppPassword](~/bot-service-manage-overview.md#microsoftappid-and-microsoftapppassword). - -### Create and configure merchant account - -1. Create and activate a Stripe account if you don't have one already. - -2. Sign in to Seller Center with your Microsoft account. - -3. Within Seller Center, connect your account with Stripe. - -4. Within Seller Center, navigate to the Dashboard and copy the value of **MerchantID**. - -5. Update the `PAYMENTS_MERCHANT_ID` environment variable to the value that you copied from the Seller Center Dashboard. - -[!INCLUDE [Payment process overview](../includes/snippet-payment-process-overview.md)] - -## Payment Bot sample - -The Payment Bot sample provides an example of a bot that sends a payment request -by using Node.js. -To see this sample bot in action, you can -try it out in web chat, -add it as a Skype -contact, or download the payment bot sample and run it locally using the Bot Framework Emulator. - -> [!NOTE] -> To complete the end-to-end payment process using the **Payment Bot** sample in web chat or Skype, -> you must specify a valid credit card or debit card within your Microsoft account -> (i.e., a valid card from a U.S. card issuer). -> Your card will not be charged and the card's CVV will not be verified, -> because the **Payment Bot** sample runs in test mode (i.e., `PAYMENTS_LIVEMODE` is set to `false` in **.env**). - -The next few sections of this article describe the three parts of the payment process, -in the context of the **Payment Bot** sample. - -## Requesting payment - -Your bot can request payment from a user by sending a message that contains a -[rich card](bot-builder-nodejs-send-rich-cards.md) with a button that specifies -`type` of "payment". -This code snippet from the **Payment Bot** sample creates a message that contains a Hero card with a **Buy** button that the user can click (or tap) to initiate the payment process. - -[!code-javascript[Request payment](../includes/code/node-request-payment.js#requestPayment)] - -In this example, the button's type is specified as `payments.PaymentActionType`, which -the app defines as "payment". -The button's value is populated by the `createPaymentRequest` function, which returns -a `PaymentRequest` object that contains information about supported payment methods, details, -and options. -For more information about implementation details, see **app.js** within the -Payment Bot sample. - -This screenshot shows the Hero card (with **Buy** button) that's generated by the code snippet above. - -![Payments sample bot](../media/payments-bot-buy.png) - -> [!IMPORTANT] -> Any user that has access to the **Buy** button may use it to initiate the payment process. -> Within the context of a group conversation, it is not possible to designate a button for use by only -> a specific user. - -## User experience - -When a user clicks the **Buy** button, he or she is directed to the payment web experience to provide all required payment, shipping, and contact information via their Microsoft account. - -![Microsoft payment](../media/microsoft-payment.png) - -### HTTP callbacks - -HTTP callbacks will be sent to your bot to indicate that it should perform certain operations. -Each callback will be an event that contains these property values: - -| Property | Value | -|----|----| -| `type` | invoke | -| `name` | Indicates the type of operation that the bot should perform (e.g., shipping address update, shipping option update, payment complete). | -| `value` | The request payload in JSON format. | -| `relatesTo` | Describes the channel and user that are associated with the payment request. | - -> [!NOTE] -> `invoke` is a special event type that is reserved for use by the Microsoft Bot Framework. -> The sender of an `invoke` event will expect your bot to acknowledge the callback by sending an HTTP response. - -## Processing callbacks - -[!INCLUDE [Process callbacks overview](../includes/snippet-payment-process-callbacks-overview.md)] - -### Shipping Address Update and Shipping Option Update callbacks - -When receiving a Shipping Address Update or a Shipping Option Update callback, -your bot will be provided with the current state of the payment details from the client in the event's `value` property. -As a merchant, you should treat these callbacks as static, given input payment details you will calculate some output payment details and -fail if the input state provided by the client is invalid for any reason.  -If the bot determines the given information is valid as-is, simply send HTTP status code `200 OK` along with the unmodified payment -details. Alternatively, the bot may send HTTP status code `200 OK` along with an updated payment details that should be applied before the order can be processed. -In some cases, your bot may determine that the updated information is invalid and the -order cannot be processed as-is. For example, the user's shipping address may specify a country to which the -product supplier does not ship. In that case, the bot may send HTTP status code `200 OK` and a message populating the error property of the payment details object. -Sending any HTTP status code in the `400` or `500` range to will result in a generic error for the customer. - -### Payment Complete callbacks - -When receiving a Payment Complete callback, your bot will be provided with a copy of the initial, unmodified payment request as -well as the payment response objects in the event's `value` property. The payment response object will contain the final selections -made by the customer along with a payment token. Your bot should take the opportunity to recalculate the final payment request based on -the initial payment request and the customer's final selections. Assuming the customer's selections are determined to be valid, the bot -should verify the amount and currency in the payment token header to ensure that they match the final payment request. If the bot -decides to charge the customer it should only charge the amount in the payment token header as this is the price the customer confirmed. -If there is a mismatch between the values that the bot expects and the values that it received in the Payment Complete callback, it can -fail the payment request by sending HTTP status code `200 OK` along with setting the result field to `failure`. - -In addition to verifying payment details, the bot should also verify that the order can be fulfilled, before it initiates payment -processing. For example, it may want to verify that the item(s) being purchased are still available in stock. -If the values are correct and your payment processor has successfully charged the payment token, then the bot should respond with HTTP -status code `200 OK` along with setting the result field to `success` in order for the payment web experience to display the payment -confirmation. The payment token that the bot receives can only be used once, by the merchant that requested it, and must be submitted to -Stripe (the only payment processor that the Bot Framework currently supports). Sending any HTTP status code in the `400` or `500` range -to will result in a generic error for the customer. - -This code snippet from the **Payment Bot** sample processes the callbacks that the bot receives. - -[!code-javascript[Request payment](../includes/code/node-request-payment.js#processCallback)] - -In this example, the bot examines the `name` property of the incoming event to identify the type of -operation it needs to perform, and then calls the appropriate method(s) to process the callback. -For more information about implementation details, see **app.js** -within the Payment Bot sample. - -## Testing a payment bot - -[!INCLUDE [Test a payment bot](../includes/snippet-payment-test-bot.md)] - -In the Payment Bot sample, the `PAYMENTS_LIVEMODE` environment variable in **.env** determines whether Payment Complete callbacks will contain emulated payment tokens or real payment tokens. If `PAYMENTS_LIVEMODE` is set to `false`, a header is added to the bot's outbound payment request to indicate that the bot is in test mode, and -the Payment Complete callback will contain an emulated payment token that cannot be charged. If `PAYMENTS_LIVEMODE` is set to `true`, the header which indicates that the bot is in test mode is omitted from the bot's outbound payment request, and the Payment Complete callback will contain a real payment token that the bot will submit to Stripe -for payment processing. This will be a real transaction that results in charges to the specified payment instrument. - -## Additional resources - -- Payment Bot sample -- [Add rich card attachments to messages](bot-builder-nodejs-send-rich-cards.md) -- Web Payments at W3C diff --git a/articles/nodejs/bot-builder-nodejs-search-azure.md b/articles/nodejs/bot-builder-nodejs-search-azure.md deleted file mode 100644 index cf488be82..000000000 --- a/articles/nodejs/bot-builder-nodejs-search-azure.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: Create data-driven experiences with Azure Search (v3 JS) - Bot Service -description: Learn how to create data-driven experiences with Azure Search and help users navigate large amounts of content in a bot with the Bot Framework SDK for Node.js and Azure Search. -author: matthewshim-ms -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Create data-driven experiences with Azure Search - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-search-azure.md) -> - [Node.js](../nodejs/bot-builder-nodejs-search-azure.md) - -You can add [Azure Search][search] to your bot to help the user navigate large amounts of content and create a data-driven exploration experience for users of your bot. - -Azure Search is an Azure service that offers keyword search, built-in linguistics, custom scoring, faceted navigation and more. Azure Search can also index content from various sources, including Azure SQL DB, DocumentDB, Blob Storage, and Table Storage. It supports "push" indexing for other sources of data, and it can open PDFs, Office documents, and other formats containing unstructured data. Once collected, the content goes into an Azure Search index, which the bot can then query. - -## Install dependencies - -From a command prompt, navigate to your bot's project directory and install the following modules with the Node Package Manager (NPM): - -* [bluebird](https://www.npmjs.com/package/bluebird) -* [lodash](https://www.npmjs.com/package/lodash) -* [request](https://www.npmjs.com/package/request) - -## Prerequisites - -The following are **required**: -- Have an Azure subscription and an Azure Search Primary Key. You can find this in the Azure portal. -- Copy the [SearchDialogLibrary](https://github.com/Microsoft/botBuilder-Samples/tree/master/Node/demo-Search/SearchDialogLibrary) library to your bot's project directory. This library contains general dialogs for the user to search, but can be customized as needed to suit your bot. - -- Copy the [SearchProviders](https://github.com/Microsoft/botBuilder-Samples/tree/master/Node/demo-Search/SearchProviders) library to your bot's project directory. This library contains all of the components required to create a request and submit it to Azure Search. - -## Connect to the Azure Service - -In your bot's main program file (e.g.: app.js), create the reference paths to the two libraries you installed previously. - -```javascript -var SearchLibrary = require('./SearchDialogLibrary'); -var AzureSearch = require('./SearchProviders/azure-search'); -``` - -Add the following sample code to your bot. In the `AzureSearch` object, pass in your own Azure Search settings into the `.create` method. At run time, this will bind your bot to the Azure Search service and wait for a completed user query in the form of a `Promise`. - -```javascript -// Azure Search -var azureSearchClient = AzureSearch.create('Your-Azure-Search-Service-Name', 'Your-Azure-Search-Primary-Key', 'Your-Azure-Search-Service-Index'); -var ResultsMapper = SearchLibrary.defaultResultsMapper(ToSearchHit); -``` - - The `azureSearchClient` reference creates the Azure Search model, which passes the Azure Service's authorization settings from the bot's `.env` settings. - `ResultsMapper` parses the Azure response object and maps the data as we define in `ToSearchHit` method. For an implementation example of this method, see [After Azure Search responds](#after-azure-search-responds). - -## Register the search library -You can customize your search dialogs directly in the `SearchLibrary` module itself. The `SearchLibrary` performs most of the heavy lifting, including making the call to Azure Search. - -Add the following code in your bot's main program file to register the Search Dialogs library with your bot. - -```javascript -bot.library(SearchLibrary.create({ - multipleSelection: true, - search: function (query) { return azureSearchClient.search(query).then(ResultsMapper); }, - refiners: ['refiner1', 'refiner2', 'refiner3'], // customize your own refiners - refineFormatter: function (refiners) { - return _.zipObject( - refiners.map(function (r) { return 'By ' + _.capitalize(r); }), - refiners); - } -})); -``` -The `SearchLibrary` not only stores all of your search related dialogs, but also takes the user query to submit to Azure Search. You will need to define your own refiners in the `refiners` array to specify entities you wish to allow your user to narrow or filter their search results. - -## Create a search dialog - -You may choose to structure your dialogs however you want. The only requirement to setting up an Azure Search dialog is to invoke the `.begin` method -from the `SearchLibrary` object, passing in the `session` object generated by the Bot Framework SDK. - -```javascript -function (session) { - // Trigger Azure Search dialogs - SearchLibrary.begin(session); - }, - function (session, args) { - // Process selected search results - session.send( - 'Search Completed!', - args.selection.map( ); // format your response - } -``` -For more information about dialogs, see [Manage a conversation with dialogs](bot-builder-nodejs-dialog-manage-conversation.md). - -## After Azure Search responds - -Once a successful Azure Search resolves, you now need to store the data you want from the response object, and display it in a meaningful way to the user. - -> [!TIP] -> Consider including the [util module][NodeUtil]. It will help you format and map the response from Azure Search. - -In your bot's main program file, create a `ToSearchHit` method. This method returns an object which formats the relevant data you need from the Azure Response. The following code shows how you can define your own parameters in the `ToSearchHit` method. - - ```javascript - function ToSearchHit(azureResponse) { - return { - // define your own parameters - key: azureResponse.id, - title: azureResponse.title, - description: azureResponse.description, - imageUrl: azureResponse.thumbnail - }; - } -``` -After this is done, all you need to do is display the data to the user. - - In the **index.js** file of the **SearchDialogLibrary** project, the `searchHitAsCard` method parses each response from the Azure Search and creates a new card object to display to the user. The fields you defined in the `ToSearchHit` method from your bot's main program file needs to synced with the properties in the `searchHitAsCard` method. - -The following shows how and where your defined parameters from the `ToSearchHit` method are used to build a card attachment UI to render to the user. - -```javascript -function searchHitAsCard(showSave, searchHit) { - var buttons = showSave - ? [new builder.CardAction().type('imBack').title('Save').value(searchHit.key)] - : []; - - var card = new builder.HeroCard() - .title(searchHit.title) - .buttons(buttons); - - if (searchHit.description) { - card.subtitle(searchHit.description); - } - - if (searchHit.imageUrl) { - card.images([new builder.CardImage().url(searchHit.imageUrl)]); - } - - return card; -} -``` - -## Sample code - -For two complete samples that show how to support Azure Search with bots using the Bot Framework SDK for Node.js, see the -[Real Estate Bot sample](https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples/Node/demo-Search/RealEstateBot) or [Job Listing Bot sample](https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples/Node/demo-Search/JobListingBot) in GitHub. - -## Additional resources - -* [Azure Search][search] -* [Node Util][NodeUtil] -* [Dialogs](bot-builder-nodejs-dialog-manage-conversation.md) - -[NodeUtil]: https://nodejs.org/api/util.html -[search]: /azure/search/search-what-is-azure-search diff --git a/articles/nodejs/bot-builder-nodejs-send-input-hints.md b/articles/nodejs/bot-builder-nodejs-send-input-hints.md deleted file mode 100644 index 66d84e2b9..000000000 --- a/articles/nodejs/bot-builder-nodejs-send-input-hints.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Add input hints to messages (v3 JS) - Bot Service -description: Learn how to add input hints to messages using the Bot Framework SDK for .NET. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add input hints to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-input-hints.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-input-hints.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-input-hints.md) - -By specifying an input hint for a message, you can indicate whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. For many channels, this enables clients to set the state of user input controls accordingly. For example, if a message's input hint indicates that the bot is ignoring user input, the client may close the microphone and disable the input box to prevent the user from providing input. - -## Accepting input - -To indicate that your bot is passively ready for input but is not awaiting a response from the user, set the message's input hint to `builder.InputHint.acceptingInput`. On many channels, this will cause the client's input box to be enabled and microphone to be closed, but still accessible to the user. For example, Cortana will open the microphone to accept input from the user if the user holds down the microphone button. The following code example creates a message that indicates the bot is accepting user input. - -[!code-javascript[IMessage.speak](../includes/code/node-input-hints.js#InputHintAcceptingInput)] - -## Expecting input - -To indicate that your bot is awaiting a response from the user, set the message's input hint to `builder.InputHint.expectingInput`. On many channels, this will cause the client's input box to be enabled and microphone to be open. The following code example creates a prompt that indicates the bot is expecting user input. - -[!code-javascript[Prompt](../includes/code/node-input-hints.js#InputHintExpectingInput)] - -## Ignoring input - -To indicate that your bot is not ready to receive input from the user, set the message's input hint to `builder.InputHint.ignoringInput`. -On many channels, this will cause the client's input box to be disabled and microphone to be closed. The following code example uses the `session.say()` method to send a message that indicates the bot is ignoring user input. - -[!code-javascript[Session.say()](../includes/code/node-input-hints.js#InputHintIgnoringInput)] - -## Default values for input hint - -If you do not set the input hint for a message, the Bot Framework SDK will automatically set it for you by using this logic: - -- If your bot sends a prompt, the input hint for the message will specify that your bot is **expecting input**. -- If your bot sends single message, the input hint for the message will specify that your bot is **accepting input**. -- If your bot sends a series of consecutive messages, the input hint for all but the final message in the series will specify that your bot is **ignoring input**, and the input hint for the final message in the series will specify that your bot is **accepting input**. - -## Additional resources - -- [Add speech to messages](bot-builder-nodejs-text-to-speech.md) diff --git a/articles/nodejs/bot-builder-nodejs-send-receive-attachments.md b/articles/nodejs/bot-builder-nodejs-send-receive-attachments.md deleted file mode 100644 index 1827796c6..000000000 --- a/articles/nodejs/bot-builder-nodejs-send-receive-attachments.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Send and receive attachments - Bot Service -description: Learn how to send and receive messages containing attachments using the Bot Framework SDK for Node.js. -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Send and receive attachments - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-media-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-receive-attachments.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-media-attachments.md) - -A message exchange between user and bot can contain media attachments, such as images, video, audio, and files. The types of attachments that can be sent varies by channel, but these are the basic types: - -* **Media and Files**: You can send files like images, audio and video by setting **contentType** to the MIME type of the [IAttachment object][IAttachment] and then passing a link to the file in **contentUrl**. -* **Cards**: You can send a rich set of visual cards by setting the **contentType** to the desired card's type and then pass the JSON for the card. If you use one of the rich card builder classes like **HeroCard**, the attachment is automatically filled in for you. See [send a rich card](bot-builder-nodejs-send-rich-cards.md) for an example of this. - -## Add a media attachment -The message object is expected to be an instance of an [IMessage][IMessage] and it's most useful to send the user a message as an object when you’d like to include an attachment like an image. Use the [session.send()][SessionSend] method to send messages in the form of a JSON object. - -## Example - -The following example checks to see if the user has sent an attachment, and if they have, it will echo back any image contained in the attachment. You can test this with the Bot Framework Emulator by sending your bot an image. - -```javascript -// Create your bot with a function to receive messages from the user -var bot = new builder.UniversalBot(connector, function (session) { - var msg = session.message; - if (msg.attachments && msg.attachments.length > 0) { - // Echo back attachment - var attachment = msg.attachments[0]; - session.send({ - text: "You sent:", - attachments: [ - { - contentType: attachment.contentType, - contentUrl: attachment.contentUrl, - name: attachment.name - } - ] - }); - } else { - // Echo back users text - session.send("You said: %s", session.message.text); - } -}); -``` -## Additional resources - -* [Channels reference][inspector] -* [IMessage][IMessage] -* [Send a rich card][SendRichCard] -* [session.send][SessionSend] - -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage -[SendRichCard]: bot-builder-nodejs-send-rich-cards.md -[SessionSend]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#send -[IAttachment]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.iattachment.html -[inspector]: ../bot-service-channels-reference.md diff --git a/articles/nodejs/bot-builder-nodejs-send-rich-cards.md b/articles/nodejs/bot-builder-nodejs-send-rich-cards.md deleted file mode 100644 index 26b9409d1..000000000 --- a/articles/nodejs/bot-builder-nodejs-send-rich-cards.md +++ /dev/null @@ -1,196 +0,0 @@ ---- -title: Add rich card attachments to messages (v3 JS) - Bot Service -description: Learn how to send interactive, engaging rich cards using the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add rich card attachments to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-rich-card-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-rich-cards.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-rich-cards.md) - -Several channels, like Skype & Facebook, support sending rich graphical cards to users with interactive buttons that the user clicks to initiate an action. -The SDK provides several message and card builder classes which can be used to create and send cards. The Bot Framework Connector Service will render these cards using schema native to the channel, supporting cross-platform communication. If the channel does not support cards, such as SMS, the Bot Framework will do its best to render a reasonable experience to users. - -## Types of rich cards - -The Bot Framework currently supports eight types of rich cards: - -| Card type | Description | -|------|------| -| [Adaptive Card](/adaptive-cards/get-started/bots) | A customizable card that can contain any combination of text, speech, images, buttons, and input fields. See [per-channel support](/adaptive-cards/get-started/bots#channel-status). | -| [Animation Card][animationCard] | A card that can play animated GIFs or short videos. | -| [Audio Card][audioCard] | A card that can play an audio file. | -| [Hero Card][heroCard] | A card that typically contains a single large image, one or more buttons, and text. | -| [Thumbnail Card][thumbnailCard] | A card that typically contains a single thumbnail image, one or more buttons, and text.| -| [Receipt Card][receiptCard] | A card that enables a bot to provide a receipt to the user. It typically contains the list of items to include on the receipt, tax and total information, and other text. | -| [Signin Card][signinCard] | A card that enables a bot to request that a user sign-in. It typically contains text and one or more buttons that the user can click to initiate the sign-in process. | -| [Video Card][videoCard] | A card that can play videos. | - -## Send a carousel of Hero cards - -The following example shows a bot for a fictional t-shirt company and shows how to send a carousel of cards in response to the user saying "show shirts". - -```javascript -// Create your bot with a function to receive messages from the user -// Create bot and default message handler -var bot = new builder.UniversalBot(connector, function (session) { - session.send("Hi... We sell shirts. Say 'show shirts' to see our products."); -}); - -// Add dialog to return list of shirts available -bot.dialog('showShirts', function (session) { - var msg = new builder.Message(session); - msg.attachmentLayout(builder.AttachmentLayout.carousel) - msg.attachments([ - new builder.HeroCard(session) - .title("Classic White T-Shirt") - .subtitle("100% Soft and Luxurious Cotton") - .text("Price is $25 and carried in sizes (S, M, L, and XL)") - .images([builder.CardImage.create(session, 'http://petersapparel.parseapp.com/img/whiteshirt.png')]) - .buttons([ - builder.CardAction.imBack(session, "buy classic white t-shirt", "Buy") - ]), - new builder.HeroCard(session) - .title("Classic Gray T-Shirt") - .subtitle("100% Soft and Luxurious Cotton") - .text("Price is $25 and carried in sizes (S, M, L, and XL)") - .images([builder.CardImage.create(session, 'http://petersapparel.parseapp.com/img/grayshirt.png')]) - .buttons([ - builder.CardAction.imBack(session, "buy classic gray t-shirt", "Buy") - ]) - ]); - session.send(msg).endDialog(); -}).triggerAction({ matches: /^(show|list)/i }); -``` - -This example uses the [Message][Message] class to build a carousel. -The carousel is comprised of a list of [HeroCard][heroCard] classes that contain an image, text, and a single button that triggers buying the item. -Clicking the **Buy** button triggers sending a message so we need to add a second dialog to catch the button click. - -## Handle button input - -The `buyButtonClick` dialog will be triggered any time a message is received that starts with "buy" or "add" and is followed by something containing the word "shirt". -The dialog starts by using a couple of regular expressions to look for the color and optional size shirt that the user asked for. -This added flexibility lets you support both button clicks and natural language messages from the user like "please add a large gray shirt to my cart". -If the color is valid but the size is unknown, the bot prompts the user to pick a size from a list before adding the item to the cart. -Once the bot has all the information it needs, it puts the item onto a cart that’s persisted using **session.userData** and then sends the user a confirmation message. - -```javascript -// Add dialog to handle 'Buy' button click -bot.dialog('buyButtonClick', [ - function (session, args, next) { - // Get color and optional size from users utterance - var utterance = args.intent.matched[0]; - var color = /(white|gray)/i.exec(utterance); - var size = /\b(Extra Large|Large|Medium|Small)\b/i.exec(utterance); - if (color) { - // Initialize cart item - var item = session.dialogData.item = { - product: "classic " + color[0].toLowerCase() + " t-shirt", - size: size ? size[0].toLowerCase() : null, - price: 25.0, - qty: 1 - }; - if (!item.size) { - // Prompt for size - builder.Prompts.choice(session, "What size would you like?", "Small|Medium|Large|Extra Large"); - } else { - //Skip to next waterfall step - next(); - } - } else { - // Invalid product - session.send("I'm sorry... That product wasn't found.").endDialog(); - } - }, - function (session, results) { - // Save size if prompted - var item = session.dialogData.item; - if (results.response) { - item.size = results.response.entity.toLowerCase(); - } - - // Add to cart - if (!session.userData.cart) { - session.userData.cart = []; - } - session.userData.cart.push(item); - - // Send confirmation to users - session.send("A '%(size)s %(product)s' has been added to your cart.", item).endDialog(); - } -]).triggerAction({ matches: /(buy|add)\s.*shirt/i }); -``` - - - -## Add a message delay for image downloads - -Some channels tend to download images before displaying a message to the user so that if you send a message containing an image followed immediately by a message without images you’ll sometimes see the messages flipped in the user's feed. To minimize the chance of this you can try to insure that your images are coming from content deliver networks (CDNs) and avoid the use of overly large images. In extreme cases you may even need to insert a 1-2 second delay between the message with the image and the one that follows it. You can make this delay feel a bit more natural to the user by calling **session.sendTyping()** to send a typing indicator before starting your delay. - - - -The Bot Framework implements a batching to try to prevent multiple messages from the bot from being displayed out of order. When your bot sends multiple replies to the user, the individual messages will be automatically grouped into a batch and delivered to the user as a set in an effort to preserve the original order of the messages. This automatic batching waits a default of 250ms after every call to **session.send()** before initiating the next call to **send()**. - -The message batching delay is configurable. To disable the SDK’s auto-batching logic, set the default delay to a large number and then manually call **sendBatch()** with a callback to invoke after the batch is delivered. - -## Send an Adaptive card - -The Adaptive Card can contain any combination of text, speech, images, buttons, and input fields. -Adaptive Cards are created using the JSON format specified in [Adaptive Cards](http://adaptivecards.io), which gives you full control over card content and format. - -To create an Adaptive Card using Node.js, leverage the information within the [Adaptive Cards](http://adaptivecards.io) site to understand Adaptive Card schema, explore Adaptive Card elements, and see JSON samples that can be used to create cards of varying composition and complexity. Additionally, you can use the Interactive Visualizer to design Adaptive Card payloads and preview card output. - -This code example shows how to create a message that contains an Adaptive Card for a calendar reminder: - -[!code-javascript[Add Adaptive Card attachment](../includes/code/node-send-card-buttons.js#addAdaptiveCardAttachment)] - -The resulting card contains three blocks of text, an input field (choice list), and three buttons: - -![Adaptive Card calendar reminder](../media/adaptive-card-reminder.png) - -## Additional resources - -- [Channels reference][inspector] -- [Adaptive Cards](http://adaptivecards.io) -- [AnimationCard][animationCard] -- [AudioCard][audioCard] -- [HeroCard][heroCard] -- [ThumbnailCard][thumbnailCard] -- [ReceiptCard][receiptCard] -- [SigninCard][signinCard] -- [VideoCard][videoCard] -- [Message][Message] -- [How to send attachments](bot-builder-nodejs-send-receive-attachments.md) - -[MessageOrder]: bot-builder-nodejs-manage-conversation-flow.md#message-ordering -[Message]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.message -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage - -[animationCard]: https://docs.microsoft.com/javascript/api/botbuilder/animationcard?view=botbuilder-ts-3.0 -[audioCard]: https://docs.microsoft.com/javascript/api/botbuilder/audiocard?view=botbuilder-ts-3.0 -[heroCard]: https://docs.microsoft.com/javascript/api/botbuilder/herocard?view=botbuilder-ts-3.0 -[thumbnailCard]: https://docs.microsoft.com/javascript/api/botbuilder/thumbnailcard?view=botbuilder-ts-3.0 -[receiptCard]: https://docs.microsoft.com/javascript/api/botbuilder/receiptcard?view=botbuilder-ts-3.0 -[signinCard]: https://docs.microsoft.com/javascript/api/botbuilder/signincard?view=botbuilder-ts-3.0 -[videoCard]: https://docs.microsoft.com/javascript/api/botbuilder/videocard?view=botbuilder-ts-3.0 - -[inspector]: ../bot-service-channels-reference.md diff --git a/articles/nodejs/bot-builder-nodejs-send-suggested-actions.md b/articles/nodejs/bot-builder-nodejs-send-suggested-actions.md deleted file mode 100644 index 8ca26e8d5..000000000 --- a/articles/nodejs/bot-builder-nodejs-send-suggested-actions.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Add suggested actions to messages (v3 JS) - Bot Service -description: Learn how to send suggested actions within messages using the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/19/2019 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add suggested actions to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-suggested-actions.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-suggested-actions.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-suggested-actions.md) - -[!INCLUDE [Introduction to suggested actions](../includes/snippet-suggested-actions-intro.md)] - -## Suggested actions example - -To add suggested actions to a message, set the `suggestedActions` property of the message to a list of [card actions][ICardAction] that represent the buttons to be presented to the user. - -This code example shows how to send a message that presents three suggested actions to the user: - -[!code-javascript[Send suggested actions](../includes/code/node-send-suggested-actions.js#sendSuggestedActions)] - -When the user taps one of the suggested actions, the bot will receive a message from the user that contains the `value` of the corresponding action. - -Be aware that the `imBack` method will post the `value` to the chat window of the channel you are using. If this is not the desired effect, you can use the `postBack` method, which will still post the selection back to your bot, but will not display the selection in the chat window. Some channels do not support `postBack`, however, and in those instances the method will behave like `imBack`. - -## Additional resources - -- [Samples][samples] -- [IMessage][IMessage] -- [ICardAction][ICardAction] -- [session.send][SessionSend] - -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage - -[SessionSend]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#send - -[ICardAction]: https://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.icardaction.html - - -[samples]: https://github.com/Microsoft/BotBuilder-Samples/tree/v3-sdk-samples diff --git a/articles/nodejs/bot-builder-nodejs-send-typing-indicator.md b/articles/nodejs/bot-builder-nodejs-send-typing-indicator.md deleted file mode 100644 index 8f3f85c6c..000000000 --- a/articles/nodejs/bot-builder-nodejs-send-typing-indicator.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Send a typing indicator - Bot Service -description: Learn how to add a "please wait" indicator to tell a user a bot is processing a request using the Bot Framework SDK for Node.js -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Send a typing indicator - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -Users expect a timely response to their messages. If your bot performs some long-running task like calling a server or executing a query without giving the user some indication that the bot heard them, the user could get impatient and send additional messages or just assume the bot is broken. -Many channels support the sending of a typing indication to show the user that the message was received and is being processed. - - -## Typing indicator example - -The following example demonstrates how to send a typing indication using [session.sendTyping()][SendTyping]. You can test this with the Bot Framework Emulator. - - -```javascript - -// Create bot and default message handler -var bot = new builder.UniversalBot(connector, function (session) { - session.sendTyping(); - setTimeout(function () { - session.send("Hello there..."); - }, 3000); -}); -``` - -Typing indicators are also useful when inserting a message delay to prevent messages that contain images from being sent out of order. - -To learn more, see [How to send a rich card](bot-builder-nodejs-send-rich-cards.md). - - -## Additional resources - -* [sendTyping][SendTyping] - - -[SendTyping]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session#sendtyping -[IMessage]: http://docs.botframework.com/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage diff --git a/articles/nodejs/bot-builder-nodejs-state-azure-cosmosdb.md b/articles/nodejs/bot-builder-nodejs-state-azure-cosmosdb.md deleted file mode 100644 index 5ef577690..000000000 --- a/articles/nodejs/bot-builder-nodejs-state-azure-cosmosdb.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Manage custom state data with Azure Cosmos DB (v3 JS) - Bot Service -description: Learn how to save and retrieve state data using Azure Cosmos DB with the Bot Framework SDK for Node.js. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage custom state data with Azure Cosmos DB for Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -In this article, you’ll implement Cosmos DB storage to store and manage your bot’s state data. The default Connector State Service used by bots is not intended for the production environment. You should either use [Azure Extensions](https://www.npmjs.com/package/botbuilder-azure) available on GitHub or implement a custom state client using data storage platform of your choice. Here are some of the reasons to use custom state storage: - -- higher state API throughput (more control over performance) -- lower-latency for geo-distribution -- control over where the data is stored (e.g.: West US vs East US) -- access to the actual state data -- state data db not shared with other bots -- store more than 32kb - -## Prerequisites - -- [Node.js](https://nodejs.org/en/). -- [Bot Framework Emulator](~/bot-service-debug-emulator.md) -- Must have a Node.js bot. If you do not have one, go [create a bot](bot-builder-nodejs-quickstart.md). - -## Create Azure account -If you don't have an Azure account, click [here](https://azure.microsoft.com/free/) to sign up for a free account. - -## Set up the Azure Cosmos DB database -1. After you’ve logged into the Azure portal, create a new *Azure Cosmos DB* database by clicking **New**. -2. Click **Databases**. -3. Find **Azure Cosmos DB** and click **Create**. -4. Fill in the fields. For the **API** field, select **SQL (DocumentDB)**. When done filling in all the fields, click the **Create** button at the bottom of the screen to deploy the new database. -5. After the new database is deployed, navigate to your new database. Click **Access keys** to find keys and connection strings. Your bot will use this information to call the storage service to save state data. - -## Install botbuilder-azure module - -To install the `botbuilder-azure` module from a command prompt, navigate to the bot's directory and run the following npm command: - -```nodejs -npm install --save botbuilder-azure -``` - -## Modify your bot code - -To use your **Azure Cosmos DB** database, add the following lines of code to your bot's **app.js** file. - -1. Require the newly installed module. - - ```javascript - var azure = require('botbuilder-azure'); - ``` - -2. Configure the connection settings to connect to Azure. - ```javascript - var documentDbOptions = { - host: 'Your-Azure-DocumentDB-URI', - masterKey: 'Your-Azure-DocumentDB-Key', - database: 'botdocs', - collection: 'botdata' - }; - ``` - The `host` and `masterKey` values can be found in the **Keys** menu of your database. If the `database` and `collection` entries do not exist in the Azure database, they will be created for you. - -3. Using the `botbuilder-azure` module, create two new objects to connect to the Azure database. First, create an instance of `DocumentDBClient` passing in the connection configuration settings (defined as `documentDbOptions` from above). Next, create an instance of `AzureBotStorage` passing in the `DocumentDBClient` object. For example: - ```javascript - var docDbClient = new azure.DocumentDbClient(documentDbOptions); - - var cosmosStorage = new azure.AzureBotStorage({ gzipData: false }, docDbClient); - ``` - -4. Specify that you want to use your custom database instead of the in-memory storage. For example: - - ```javascript - var bot = new builder.UniversalBot(connector, function (session) { - // ... Bot code ... - }) - .set('storage', cosmosStorage); - ``` - -Now you are ready to test the bot with the emulator. - -## Run your bot app - -From a command prompt, navigate to your bot's directory and run your bot with the following command: - -```nodejs -node app.js -``` - -## Connect your bot to the emulator - -At this point, your bot is running locally. Start the emulator and then connect to your bot from the emulator: - -1. Type http://localhost:port-number/api/messages into the emulator's address bar, where port-number matches the port number shown in the browser where your application is running. You can leave Microsoft App ID and Microsoft App Password fields blank for now. You'll get this information later when you [register your bot](~/bot-service-quickstart-registration.md). -2. Click **Connect**. -3. Test your bot by sending your bot a message. Interact with your bot as you normally would. When you are done, go to **Storage Explorer** and view your saved state data. - -## View state data on Azure Portal - -To view the state data, sign into your Azure portal and navigate to your database. Click **Data Explorer (preview)** to verify that the state information from your bot is being saved. - -## Next step - -Now that you have full control over your bot's state data, let's take a look at how you can use it to better manage conversation flow. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-nodejs-dialog-manage-conversation-flow.md) diff --git a/articles/nodejs/bot-builder-nodejs-state-azure-table-storage.md b/articles/nodejs/bot-builder-nodejs-state-azure-table-storage.md deleted file mode 100644 index acdf7557a..000000000 --- a/articles/nodejs/bot-builder-nodejs-state-azure-table-storage.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: Manage custom state data with Azure Table storage (v3 JS) - Bot Service -description: Learn how to save and retrieve state data using Azure Table storage with the Bot Framework SDK for Node.js. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage custom state data with Azure Table storage for Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -In this article, you’ll implement Azure Table storage to store and manage your bot’s state data. The default Connector State Service used by bots is not intended for the production environment. You should either use [Azure Extensions](https://www.npmjs.com/package/botbuilder-azure) available on GitHub or implement a custom state client using data storage platform of your choice. Here are some of the reasons to use custom state storage: - -- higher state API throughput (more control over performance) -- lower-latency for geo-distribution -- control over where the data is stored (e.g.: West US vs East US) -- access to the actual state data -- state data db not shared with other bots -- store more than 32kb - -## Prerequisites - -- [Node.js](https://nodejs.org/en/). -- [Bot Framework Emulator](~/bot-service-debug-emulator.md). -- Must have a Node.js bot. If you do not have one, go [create a bot](bot-builder-nodejs-quickstart.md). -- [Storage Explorer](http://storageexplorer.com/). - -## Create Azure account -If you don't have an Azure account, click [here](https://azure.microsoft.com/free/) to sign up for a free account. - -## Set up the Azure Table storage service -1. After you’ve logged into the Azure portal, create a new Azure Table storage service by clicking on **New**. -2. Search for **Storage account** that implements the Azure Table. Click **Create** to start creating the storage account. -3. Fill in the fields, click the **Create** button at the bottom of the screen to deploy the new storage service. -4. After the new storage service is deployed, navigate to the storage account you just created. You can find it listed in the **Storage Accounts** blade. -4. Select **Access keys**, and copy the key for later use. Your bot will use **Storage account name** and **Key** to call the storage service to save state data. - -## Install botbuilder-azure module - -To install the `botbuilder-azure` module from a command prompt, navigate to the bot's directory and run the following npm command: - -```nodejs -npm install --save botbuilder-azure -``` - -## Modify your bot code - -To use your **Azure Table** storage, add the following lines of code to your bot's **app.js** file. - -1. Require the newly installed module. - - ```javascript - var azure = require('botbuilder-azure'); - ``` - -2. Configure the connection settings to connect to Azure. - ```javascript - // Table storage - var tableName = "Table-Name"; // You define - var storageName = "Table-Storage-Name"; // Obtain from Azure Portal - var storageKey = "Azure-Table-Key"; // Obtain from Azure Portal - ``` - The `storageName` and `storageKay` values can be found in the **Access keys** menu of your Azure Table. If the `tableName` does not exist in the Azure Table, it will be created for you. - -3. Using the `botbuilder-azure` module, create two new objects to connect to the Azure Table. First, create an instance of `AzureTableClient` passing in the connection configuration settings. Next, create an instance of `AzureBotStorage` passing in the `AzureTableClient` object. For example: - - ```javascript - var azureTableClient = new azure.AzureTableClient(tableName, storageName, storageKey); - - var tableStorage = new azure.AzureBotStorage({gzipData: false}, azureTableClient); - ``` - -4. Specify that you want to use your custom database instead of the in-memory storage and add session information to database. For example: - - ```javascript - var bot = new builder.UniversalBot(connector, function (session) { - // ... Bot code ... - - // capture session user information - session.userData = {"userId": session.message.user.id, "jobTitle": "Senior Developer"}; - - // capture conversation information - session.conversationData[timestamp.toISOString().replace(/:/g,"-")] = session.message.text; - - // save data - session.save(); - }) - .set('storage', tableStorage); - ``` -Now you are ready to test the bot with the emulator. - -## Run your bot app - -From a command prompt, navigate to your bot's directory and run your bot with the following command: - -```nodejs -node app.js -``` - -## Connect your bot to the emulator - -At this point, your bot is running locally. Start the emulator and then connect to your bot from the emulator: - -1. Type http://localhost:port-number/api/messages into the emulator's address bar, where port-number matches the port number shown in the browser where your application is running. You can leave Microsoft App ID and Microsoft App Password fields blank for now. You'll get this information later when you [register your bot](~/bot-service-quickstart-registration.md). -2. Click **Connect**. -3. Test your bot by sending your bot a message. Interact with your bot as you normally would. When you are done, go to **Storage Explorer** and view your saved state data. - -## View data in Storage Explorer - -To view the state data, open **Storage Explorer** and connect to Azure using your Azure Portal credential or connect directly to the Table using the `storageName` and `storageKey` and navigate to your `tableName`. - -![Screenshot of Storage Explorer with botdata table rows](~/media/bot-builder-nodejs-state-azure-table-storage/bot-builder-nodejs-state-azure-table-storage-query.png) - -One record of the conversation in the **data** column looks like: - -```JSON -{ - "2018-05-15T18-23-48.780Z": "I'm the second user", - "2018-05-15T18-23-55.120Z": "Do you know what time it is?", - "2018-05-15T18-24-12.214Z": "I'm looking for information about the new process." -} -``` - -## Next step - -Now that you have full control over your bot's state data, let's take a look at how you can use it to better manage conversation flow. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-nodejs-dialog-manage-conversation-flow.md) diff --git a/articles/nodejs/bot-builder-nodejs-state.md b/articles/nodejs/bot-builder-nodejs-state.md deleted file mode 100644 index 785f6581a..000000000 --- a/articles/nodejs/bot-builder-nodejs-state.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: Manage state data (v3 JS) - Bot Service -description: Learn how to save and retrieve state data with the Bot Framework SDK for Node.js. -author: DucVo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Manage state data - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-state.md) -> - [Node.js](../nodejs/bot-builder-nodejs-state.md) - -[!INCLUDE [State concept overview](../includes/snippet-dotnet-concept-state.md)] - -## In-memory data storage - -In-memory data storage is intended for testing only. This storage is volatile and temporary. The data is cleared each time the bot is restarted. To use the in-memory storage for testing purposes, you will need to do two things. First create a new instance of the in-memory storage: - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); -``` - -Then, set it to the bot when you create the **UniversalBot**: - -```javascript -var inMemoryStorage = new builder.MemoryBotStorage(); -var bot = new builder.UniversalBot(connector, [..waterfall steps..]) - .set('storage', inMemoryStorage); // Register in-memory storage -``` - -You can use this method to set your own custom data storage or use any of the *Azure Extensions*. - -## Manage custom data storage - -For performance and security reasons in the production environment, you may implement your own data storage or consider implementing one of the following data storage options: - -1. [Manage state data with Cosmos DB](bot-builder-nodejs-state-azure-cosmosdb.md) - -2. [Manage state data with Table storage](bot-builder-nodejs-state-azure-table-storage.md) - -With either of these [Azure Extensions](https://www.npmjs.com/package/botbuilder-azure) options, the mechanism for setting and persisting data via the Bot Framework SDK for Node.js remains the same as the in-memory data storage. - -## Storage containers - -In the Bot Framework SDK for Node.js, the `session` object exposes the following properties for storing state data. - -| Property | Scoped to | Description | -| ---- | ---- | ---- | -| [`userData`][userDataURL] | User | Contains data that is saved for the user on the specified channel. This data will persist across multiple conversations. | -| [`privateConversationData`][privateConversationDataURL] | Conversation | Contains data that is saved for the user within the context of a particular conversation on the specified channel. This data is private to the current user and will persist for the current conversation only. The property is cleared when the conversation ends or when `endConversation` is called explicitly. | -| [`conversationData`][conversationDataURL] | Conversation | Contains data that is saved in the context of a particular conversation on the specified channel. This data is shared with all users participating in the conversation and will persist for the current conversation only. The property is cleared when the conversation ends or when `endConversation` is called explicitly. | -| [`dialogData`][dialogDataURL] | Dialog | Contains data that is saved for the current dialog only. Each dialog maintains its own copy of this property. The property is cleared when the dialog is removed from the dialog stack. | - -These four properties correspond to the four data storage containers that can be used to store data. Which properties you use to store data will depend upon the appropriate scope for the data you are storing, the nature of the data that you are storing, and how long you want the data to persist. For example, if you need to store user data that will be available across multiple conversations, consider using the `userData` property. If you need to temporarily store local variable values within the scope of a dialog, consider using the `dialogData` property. If you need to temporarily store data that must be accessible across multiple dialogs, consider using the `conversationData` property. - -## Data Persistence - -By default, data that is stored using the `userData`, `privateConversationData`, and `conversationData` properties is set to persist after the conversation ends. If you do not want the data to persist in the `userData` container, set the `persistUserData` flag to **false**. If you do not want the data to persist in the `conversationData` container, set the `persistConversationData` flag to **false**. - -```javascript -// Do not persist userData -bot.set(`persistUserData`, false); - -// Do not persist conversationData -bot.set(`persistConversationData`, false); -``` - -> [!NOTE] -> You cannot disable data persistence for the `privateConversationData` container; it is always persisted. - -## Set data - -You can store simple JavaScript objects by saving them directly to a storage container. For a complex object like `Date`, consider converting it to `string`. This is because state data is serialized and stored as JSON. The following code samples show how to store primitive data, an array, an object map, and a complex `Date` object. - -**Store primitive data** - -```javascript -session.userData.userName = "Kumar Sarma"; -session.userData.userAge = 37; -session.userData.hasChildren = true; -``` - -**Store an array** - -```javascript -session.userData.profile = ["Kumar Sarma", "37", "true"]; -``` - -**Store an object map** - -```javascript -session.userData.about = { - "Profile": { - "Name": "Kumar Sarma", - "Age": 37, - "hasChildren": true - }, - "Job": { - "Company": "Contoso", - "StartDate": "June 8th, 2010", - "Title": "Developer" - } -} -``` -**Store Date and Time** - -For a complex JavaScript object, convert it to a string before saving to storage container. - -```javascript -var startDate = builder.EntityRecognizer.resolveTime([results.response]); - -// Date as string: "2017-08-23T05:00:00.000Z" -session.userdata.start = startDate.toISOString(); -``` - -### Saving data - -Data that is created in each storage container will remain in memory until the container is saved. The Bot Framework SDK for Node.js sends data to the `ChatConnector` service in batches to be saved when there are messages to be sent. To save the data that exists in the storage containers without sending any messages, you can manually call the [`save`](https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#save) method. If you do not call the `save` method, the data that exists in the storage containers will be persisted as part of the batch processing. - -```javascript -session.userData.favoriteColor = "Red"; -session.userData.about.job.Title = "Senior Developer"; -session.save(); -``` - -## Get data - -To access the data that is saved in a particular storage container, simply reference the corresponding property. The following code samples show how to access data that was previously stored as primitive data, an array, an object map, and a complex Date object. - -**Access primitive data** - -```javascript -var userName = session.userData.userName; -var userAge = session.userData.userAge; -var hasChildren = session.userData.hasChildren; -``` - -**Access an array** - -```javascript -var userProfile = session.userData.userProfile; - -session.send("User Profile:"); -for(int i = 0; i < userProfile.length, i++){ - session.send(userProfile[i]); -} -``` - -**Access an object map** - -```javascript -var about = session.userData.about; - -session.send("User %s works at %s.", about.Profile.Name, about.Job.Company); -``` - -**Access a Date object** - -Retrieve date data as string then convert it into a JavaScript's Date object. - -```javascript -// startDate as a JavaScript Date object. -var startDate = new Date(session.userdata.start); -``` - -## Delete data - -By default, data that is stored in the `dialogData` container is cleared when a dialog is removed from the dialog stack. Likewise, data that is stored in the `conversationData` container and `privateConversationData` container is cleared when the `endConversation` method is called. However, to delete data stored in the `userData` container, you have to explicitly clear it. - -To explicitly clear the data that is stored in any of the storage containers, simply reset the container as shown in the following code sample. - -```javascript -// Clears data stored in container. -session.userData = {}; -session.privateConversationData = {}; -session.conversationData = {}; -session.dialogData = {}; -``` - -Never set a data container `null` or remove it from the `session` object, as doing so will cause errors the next time you try to access the container. Also, you may want to manually call `session.save();` after you manually clear a container in memory, to clear any corresponding data that has previously been persisted. - -## Next steps - -Now that you understand how to manage user state data, let's take a look at how you can use it to better manage conversation flow. - -> [!div class="nextstepaction"] -> [Manage conversation flow](bot-builder-nodejs-dialog-manage-conversation-flow.md) - -## Additional resources -- [Prompt user for input](bot-builder-nodejs-dialog-prompt.md) - -[userDataURL]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#userdata -[conversationDataURL]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#conversationdata -[privateConversationDataURL]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#privateconversationdata -[dialogDataURL]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.session.html#dialogdata - -[ChatConnector]: https://docs.botframework.com/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html diff --git a/articles/nodejs/bot-builder-nodejs-text-to-speech.md b/articles/nodejs/bot-builder-nodejs-text-to-speech.md deleted file mode 100644 index f13ceec09..000000000 --- a/articles/nodejs/bot-builder-nodejs-text-to-speech.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Add speech to messages (v3 JS) - Bot Service -description: Learn how to add speech to messages using the Bot Framework SDK for Node.js. -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 -monikerRange: 'azure-bot-service-3.0' ---- - -# Add speech to messages - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-text-to-speech.md) -> - [Node.js](../nodejs/bot-builder-nodejs-text-to-speech.md) -> - [REST](../rest-api/bot-framework-rest-connector-text-to-speech.md) - -If you are building a bot for a speech-enabled channel such as Cortana, you can construct messages that specify the text to be spoken by your bot. You can also attempt to influence the state of the client's microphone by specifying an [input hint](bot-builder-nodejs-send-input-hints.md) to indicate whether your bot is accepting, expecting, or ignoring user input. - -## Specify text to be spoken by your bot - -Using the Bot Framework SDK for Node.js, there are multiple ways to specify the text to be spoken by your bot on a speech-enabled channel. You can set the `IMessage.speak` property and send the message using the `session.send()` method, send the message using the `session.say()` method (passing parameters that specify display text, speech text, and options), or send the message using a built-in prompt (specifying options `speak` and `retrySpeak`). - -### IMessage.speak - -If you are creating a message that will be sent using the `session.send()` method, set the `speak` property to specify the text to be spoken by your bot. The following code example creates a message that specifies text to be spoken and indicates that the bot is [accepting user input](bot-builder-nodejs-send-input-hints.md). - -[!code-javascript[IMessage.speak](../includes/code/node-text-to-speech.js#IMessageSpeak)] - -### session.say() - -As an alternative to using `session.send()`, you can call the `session.say()` method to create and send a message that specifies the text to be spoken, in addition to the text to be displayed and other options. The method is defined as follows: - -`session.say(displayText: string, speechText: string, options?: object)` - -| Parameter | Description | -|----|----| -| `displayText` | The text to be displayed. | -| `speechText` | The text (in plain text or SSML format) to be spoken. | -| `options` | An `IMessage` object that can contain an attachment or [input hint](bot-builder-nodejs-send-input-hints.md). | - -The following code example sends a message that specifies text to be displayed and text to be spoken and indicates that the bot is [ignoring user input](bot-builder-nodejs-send-input-hints.md). - -[!code-javascript[Session.say()](../includes/code/node-text-to-speech.js#SessionSay)] - -### Prompt options - -Using any of the built-in prompts, you can set the options `speak` and `retrySpeak` to specify the text to be spoken by your bot. The following code example creates a prompt that specifies text to be displayed, text to be spoken initially, and text to be spoken after waiting a while for user input. It indicates that the bot is [expecting user input](bot-builder-nodejs-send-input-hints.md) and uses [SSML](#ssml) formatting to specify that the word "sure" should be spoken with a moderate amount of emphasis. - -[!code-javascript[Prompt](../includes/code/node-text-to-speech.js#Prompt)] - -## Speech Synthesis Markup Language (SSML) - -To specify text to be spoken by your bot, you can use either a plain text string or a string that is formatted as Speech Synthesis Markup Language (SSML), an XML-based markup language that enables you to control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, pitch, and more. For details about SSML, see Speech Synthesis Markup Language Reference. - -> [!TIP] -> Use an SSML library to create well-formatted SSML. - -## Input hints - -When you send a message on a speech-enabled channel, you can attempt to influence the state of the client's microphone by also including an input hint to indicate whether your bot is accepting, expecting, or ignoring user input. For more information, see [Add input hints to messages](bot-builder-nodejs-send-input-hints.md). - -## Sample code - -For a complete sample that shows how to create a speech-enabled bot using the Bot Framework SDK for Node.js, see the Roller sample in GitHub. - -## Additional resources - -- Speech Synthesis Markup Language (SSML) -- Roller sample (GitHub) diff --git a/articles/nodejs/cortana-skill-concepts.md b/articles/nodejs/cortana-skill-concepts.md deleted file mode 100644 index a76d47b5e..000000000 --- a/articles/nodejs/cortana-skill-concepts.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: Building a Cortana skill using Node.js - Bot Service -description: Learn core concepts for building a Cortana skill in the Bot Framework SDK for Node.js. -keywords: Bot Framework, Cortana skill, speech, Node.js, Bot Builder, SDK, key concepts, core concepts -author: DeniseMak -manager: kamrani -ms.author: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/10/2019 -monikerRange: 'azure-bot-service-3.0' -#ROBOTS: Index ---- - -# Key concepts for building a bot for Cortana skills using Node.js - -[!INCLUDE [pre-release-label](../includes/pre-release-label-v3.md)] - -> [!NOTE] -> This article is preliminary content and will be updated. - -This article introduces key concepts for building a Cortana skill in the Bot Framework SDK for Node.js. - -## What is a Cortana skill? -A Cortana skill is a bot you can invoke by using a Cortana client, like the one built in to Windows 10. The user launches the bot by saying some keywords or phrases associated with the bot. You use the Bot Framework Portal to configure which keywords are used to launch your bot. - -Cortana can be thought of as speech-enabled channel that can send and receive voice messages in addition to textual conversation. A bot that is published as a Cortana skill should be designed for speech as well as text. The Bot Framework provides methods for specifying Speech Synthesis Markup Language (SSML) to define spoken messages that your bot sends. - -## Acknowledge user utterances - - - - - -When you create a speech-enabled bot, you should try to establish common ground and mutual understanding in the conversation. -The bot should "ground" the user's utterances by indicating that the user was heard and understood. - -Users get confused if a system fails to ground their utterances. For example, the following conversation can be a bit confusing when the bot asks "What's next?": - -> **Cortana**: Did you want to review some more of your profile? -> **User**: No. -> **Cortana**: What's next? - -If the bot adds an "Okay" as acknowledgment, it's friendlier for the user: - -> **Cortana**: Did you want to review some more of your profile? -> **User**: No. -> **Cortana**: **Okay**, what's next? - -Degrees of grounding, from weakest to strongest: - -1. Continued attention -2. Next relevant contribution -3. Acknowledgment: Minimal response or continuer: "yeah", "uh-huh", "okay", "great" -4. Demonstrate: Indicate understanding by reformulation, completion. -5. Display: Repeat all or part. - -### Acknowledgement and next relevant contribution - -> **User**: I need to travel in May. -> **Cortana**: **Okay**. What day in May did you want to travel? -> **User**: Well, I need to be there from the 12th to the 15th? -> **Cortana**: **Okay**. What city are you flying into ? - -### Grounding by demonstration - -> **User**: I need to travel in May. -> **Cortana**: And, **what day** in May did you want to travel? -> **User**: Okay, I need to be there from the 12th to the 15th? -> **Cortana**: **And** you're flying into what city? - -### Closure - -The bot performing an action should present evidence of successful performance. It's also important to indicate failure or understanding. - -* Non-speech closure: If you push an elevator button, its light turns on. -This is two step process: - * Presentation (when you press the button) - * Acceptance (when the button lights up) - -## Differences in content presentation -Keep in mind that Cortana is supported on a variety of devices, only some of which have screens. One of the things you need to consider when designing your speech-enabled bot is that the spoken dialogue often will not be the same as the text messages your bot displays. - -```javascript - var choices = [ - { - value: 'flipCoinDialog', - action: { title: "Flip A Coin" }, - synonyms: 'toss coin|flip quarter|toss quarter' - }, - { - value: 'rollDiceDialog', - action: { title: "Roll Dice" }, - synonyms: 'roll die|shoot dice|shoot die' - }, - { - value: 'magicBallDialog', - action: { title: "Magic 8-Ball" }, - synonyms: 'shake ball' - }, - { - value: 'quit', - action: { title: "Quit" }, - synonyms: 'exit|stop|end' - } - ]; - builder.Prompts.choice(session, "Decision Options", choices, { - listStyle: builder.ListStyle.button, - speak: ssml.speak("How would you like me to decide?") - }); -``` - -## Configuring your bot - -## Prompts - -## Additional resources - -Cortana documentation: [Cortana Skills Documentation](/cortana/skills/) - -Cortana SSML reference: [Speech Synthesis Markup Language (SSML) reference](/cortana/skills/speech-synthesis-markup-language) diff --git a/articles/policy-reference.md b/articles/policy-reference.md new file mode 100644 index 000000000..ec0c20fdb --- /dev/null +++ b/articles/policy-reference.md @@ -0,0 +1,35 @@ +--- +title: Built-in policy definitions for Azure AI Bot Service +description: Lists Azure Policy built-in policy definitions for Azure AI Bot Service. These built-in policy definitions provide common approaches to managing your Azure resources. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - subject-policy-reference + - evergreen +--- +# Azure Policy built-in definitions for Azure AI Bot Service + +This page is an index of [Azure Policy](/azure/governance/policy/overview) built-in policy +definitions for Azure AI Bot Service. For additional Azure Policy built-ins for other services, see +[Azure Policy built-in definitions](/azure/governance/policy/samples/built-in-policies). + +The name of each built-in policy definition links to the policy definition in the Azure portal. Use +the link in the **Version** column to view the source on the +[Azure Policy GitHub repo](https://github.com/Azure/azure-policy). + +## Azure AI Bot Service + +|Name
(Azure portal) |Description |Effect(s) |Version
(GitHub) | +|---|---|---|---| +|[Bot Service endpoint should be a valid HTTPS URI](https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F6164527b-e1ee-4882-8673-572f425f5e0a) |Data can be tampered with during transmission. Protocols exist that provide encryption to address problems of misuse and tampering. To ensure your bots are communicating only over encrypted channels, set the endpoint to a valid HTTPS URI. This ensures the HTTPS protocol is used to encrypt your data in transit and is also often a requirement for compliance with regulatory or industry standards. Please visit: [/azure/bot-service/bot-builder-security-guidelines](/azure/bot-service/bot-builder-security-guidelines). |audit, deny, disabled |[1.0.1](https://github.com/Azure/azure-policy/blob/master/built-in-policies/policyDefinitions/Bot%20Service/BotService_ValidEndpoint_Audit.json) | +|[Bot Service should be encrypted with a customer-managed key](https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F51522a96-0869-4791-82f3-981000c2c67f) |Azure AI Bot Service automatically encrypts your resource to protect your data and meet organizational security and compliance commitments. By default, Microsoft-managed encryption keys are used. For greater flexibility in managing keys or controlling access to your subscription, select customer-managed keys, also known as bring your own key (BYOK). Learn more about Azure AI Bot Service encryption: [/azure/bot-service/bot-service-encryption](bot-service-encryption.md). |audit, deny, disabled |[1.0.0](https://github.com/Azure/azure-policy/blob/master/built-in-policies/policyDefinitions/Bot%20Service/BotService_CMKEnabled_Audit.json) | + +## Next steps + +- See the built-ins on the [Azure Policy GitHub repo](https://github.com/Azure/azure-policy). +- Review the [Azure Policy definition structure](/azure/governance/policy/concepts/definition-structure). +- Review [Understanding policy effects](/azure/governance/policy/concepts/effects). diff --git a/articles/powershell/bot-builder-powershell-quickstart.md b/articles/powershell/bot-builder-powershell-quickstart.md new file mode 100644 index 000000000..a4dae1d64 --- /dev/null +++ b/articles/powershell/bot-builder-powershell-quickstart.md @@ -0,0 +1,164 @@ +--- +title: Publish a bot with Azure PowerShell - Azure AI Bot Service +description: Learn how to publish a bot with Azure PowerShell. +author: iaanw +ms.author: iawilt +manager: leeclontz +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - devx-track-azurepowershell + - modeapi + - evergreen +--- + +# Create and publish a bot with Azure PowerShell + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article shows you how to use Azure PowerShell to create a bot and register it with Azure using an existing Microsoft Entra ID application registration. + +Use an **Azure Bot** resource to host your bot. +You'll create and develop your bot locally and host it on Azure or a different platform. Follow the steps described in how-to [Register a bot with Azure](../bot-service-quickstart-registration.md). When you register your bot, you provide the web address where your bot is hosted. You can still host it in Azure. + + +You can run these commands locally, using Azure PowerShell, or remotely through the Azure portal, using Azure CloudShell. For more information about Azure CloudShell, see the [Overview of Azure Cloud Shell](/azure/cloud-shell/overview). + +> [!IMPORTANT] +> While the **Az.BotService** PowerShell module is in preview, you must install it separately using the `Install-Module` cmdlet. + +Creating a bot with Azure AI Bot Service and creating a bot locally are independent, parallel ways to create a bot. + +## Prerequisites + +- If you don't have an Azure subscription, create a [free](https://azure.microsoft.com/free/) + account before you begin. + +- An existing Microsoft Entra ID application registration that can be used from any Microsoft Entra ID tenant. + - To complete this quickstart, you'll need the app ID and secret for the application registration. + +- [Install the Az PowerShell module](/powershell/azure/install-az-ps). This is required because the Az.BotService module is in preview. + + ```azurepowershell-interactive + Install-Module -Name Az.BotService -AllowClobber + ``` + +- If you choose to use Azure PowerShell locally: + - Connect to your Azure account using the + [Connect-AzAccount](/powershell/module/az.accounts/connect-azaccount) cmdlet. + +## Choose your subscription + +If you have multiple Azure subscriptions, choose the appropriate subscription in which the resources should be billed. + +1. To list the subscriptions you can access, use the [Get-AzSubscription](/powershell/module/az.accounts/get-azsubscription) cmdlet. + + ```azurepowershell-interactive + Get-AzSubscription + ``` + +1. Set the specific subscription using the [Set-AzContext](/powershell/module/az.accounts/set-azcontext) cmdlet. + + You should use the same subscription for your bot as for the application registration. + + ```azurepowershell-interactive + Set-AzContext -SubscriptionId "" + ``` + +## Create a resource group + +If you don't already have an [Azure resource group](/azure/azure-resource-manager/management/overview) you want to use for your bot, create a new one using the [New-AzResourceGroup](/powershell/module/az.resources/new-azresourcegroup) cmdlet. + +- A resource group is a logical container in which Azure resources are deployed and managed as a group. + +The following example creates a resource group with the specified name and in the specified location. + +```azurepowershell-interactive +New-AzResourceGroup -Name -Location +``` + +## Create a new bot service + +To create a new bot service for your bot, you use the [New-AzBotService](/powershell/module/az.botservice/new-azbotservice) +cmdlet. The following example creates a new bot service with the specified values. + +```azurepowershell-interactive +New-AzBotService -ResourceGroupName -Name -ApplicationId -Location -Sku S1 -Description "" -Webapp +``` + + + + +To retrieve the status of a bot service, you use the +[Get-AzBotService](/powershell/module/az.botservice/get-azbotservice) cmdlet. The following example +gets a list of all the resources in the specified resource group. + + +```azurepowershell-interactive +Get-AzBotService -ResourceGroupName +``` + +## Initialize project folder + +To initialize the project file folder, you use the +[Initialize-AzBotServicePrepareDeploy](/powershell/module/az.botservice/initialize-azbotservicepreparedeploy) +cmdlet. The following example initializes the specified file in the specified folder. + + +```azurepowershell-interactive +Initialize-AzBotServicePrepareDeploy -CodeDir C:\tmp\MyEchoBot -ProjFileName MyEchoBot.csproj +``` + +## Publish bot service to Azure + +To publish your bot service to Azure, you use the +[Publish-AzBotServiceApp](/powershell/module/az.botservice/publish-azbotserviceapp) cmdlet. The +following example publishes the specified bot service to Azure. + +```azurepowershell-interactive +Publish-AzBotServiceApp -ResourceGroupName myResourceGroup -CodeDir D:\tmp\MyEchoBot -Name MyEchoBot +``` + +## Download code + +To download the code to work on it locally, you use the +[Export-AzBotServiceApp](/powershell/module/az.botservice/export-azbotserviceapp) cmdlet. The +following example downloads the code for the specified bot service app in the specified resource +group. + +```azurepowershell-interactive +Export-AzBotServiceApp -ResourceGroupName myResourceGroup -Name MyEchoBot +``` + +## Clean up resources + +If the resources created in this article aren't needed, you can delete them by running the following +examples. + +### Delete the Bot Service + +To delete the Bot Service from the resource group, you use the +[Remove-AzBotService](/powershell/module/az.botservice/remove-azbotservice) +cmdlet. The following example deletes the bot service from the specified resource group. + +```azurepowershell-interactive +Remove-AzBotService -Name MyEchoBot -ResourceGroupName myResourceGroup +``` + +### Delete the resource group + +> [!CAUTION] +> The following example deletes the specified resource group and all resources contained within it. +> If resources outside the scope of this article exist in the specified resource group, they'll +> also be deleted. + +```azurepowershell-interactive +Remove-AzResourceGroup -Name myResourceGroup +``` + +## Next steps + +After you download the code, you can continue to develop the bot locally on your machine. Once you +test your bot and are ready to upload the bot code to the Azure portal, follow the instructions +listed under [set up continuous deployment](../bot-service-build-continuous-deployment.md) topic to +automatically update code after you make changes. diff --git a/articles/provision-and-publish-a-bot.md b/articles/provision-and-publish-a-bot.md new file mode 100644 index 000000000..bc7c746a6 --- /dev/null +++ b/articles/provision-and-publish-a-bot.md @@ -0,0 +1,250 @@ +--- +title: Provision and publish a bot in Azure +description: Learn how to create Azure resources and publish your bot to Azure. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - devx-track-azurecli + - evergreen +--- + +# Provision and publish a bot + +[!INCLUDE [applies-to-v4](./includes/applies-to-v4-current.md)] + +This article describes how to use the Azure CLI to create resources for your bot, prepare your bot for deployment, and deploy your bot to Azure. + +This article assumes that you have a bot ready to be deployed. For information on how to create a simple echo bot, see [Create a bot with the Bot Framework SDK](bot-service-quickstart-create-bot.md). You can also use one of the samples provided in the [Bot Framework Samples repository](https://github.com/microsoft/BotBuilder-Samples#readme). + +> [!TIP] +> This article creates an Azure Bot resource for your bot. +> Existing bots that use a Web App Bot resource or a Bot Channels Registration resource will continue to work, but you can't create new bots that use these resource types. + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] + +## Prerequisites + +- For Java bots, install [Maven](https://maven.apache.org/). +- This process uses two Azure Resource Manager templates (ARM templates) to create resources for your bot. + + If you don't have the current templates, create a copy in your bot project of the **deploymentTemplates** folder: [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates/Microsoft.BotFramework.CSharp.EchoBot/content), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder/generators/app/templates/echo), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators/app/templates/echo/%7B%7Bcookiecutter.bot_name%7D%7D), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators/generators/app/templates/echo/project). + +[!INCLUDE [Azure CLI prerequisites](./includes/az-cli/prereqs.md)] + +> [!NOTE] +> If your bot uses additional resources, such as a storage service or language services, these need to be deployed separately. + +## Plan your deployment + +Before you begin, make these decisions. + +| Decision | Notes | +|:-|:-| +| How you'll manage the identities of your bot resources in Azure | You can use a user-assigned managed identity, a single-tenant app registration, or a mutli-tenant app registration. For more information, see [Create an identity resource](#create-an-identity-resource). | +| In which resource group or resource groups you'll create your bot resources | Until you're familiar with this process, we recommend using one resource group. For more information, see [Manage Azure resources](/azure/azure-resource-manager/management/). | +| Whether your bot will be _regional_ or _global_ | For information about regional bots, see [Regionalization in Azure AI Bot Service](v4sdk/bot-builder-concept-regionalization.md). | + +[!INCLUDE [Note about support for each identity app type](includes/azure-bot-resource/identity-app-type-support.md)] + +> [!IMPORTANT] +> Python bots can't be deployed to a resource group that contains Windows services or bots. +> However, multiple Python bots can be deployed to the same resource group. +> Create other services, such as Azure AI services, in a different resource group. + +### Azure resources + +Before you can deploy your bot, you create (or _provision_) the Azure resources it will need. +For some of the steps, you can use an existing resource or create a new one. + +You may find it helpful to decide ahead of time on the names of the new resources you'll create and the names of the existing resources you'll use. +Your bot will use these types of resources. + +- The Azure subscription that you'll use to provision, publish, and manage the bot +- One or more resource groups +- A user-assigned managed identity _or_ an Microsoft Entra ID app registration +- An App Service Plan resource +- An App Service resource +- An Azure Bot resource + +### Information used across resources + +As you create resources in Azure, Azure will generate or request IDs, passwords, and other information that you'll need in later steps. +The following table lists the information beyond resource names you'll need to record, in which step it's generated, and in which steps it's used. + +> [!CAUTION] +> Many of these IDs and passwords are sensitive information. For information about common security guidelines, see [Bot Framework security guidelines](v4sdk/bot-builder-security-guidelines.md). + +### [User-assigned managed identity](#tab/userassigned) + +| Information | Where generated or found | Where used | +|:-|:-|:-| +| Tenant ID | [Sign in and select subscription](#sign-in-and-select-subscription) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| App type | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| Client ID | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| Base app service URL | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md) | +| App Service name | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Publish your bot to Azure](#publish-your-bot-to-azure) | + +### [Single-tenant](#tab/singletenant) + +| Information | Where generated or found | Where used | +|:-|:-|:-| +| Tenant ID | [Sign in and select subscription](#sign-in-and-select-subscription) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| App type | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| App ID | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md) | +| App password | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Update project configuration settings](#update-project-configuration-settings) | +| Base app service URL | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md) | +| App Service name | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Publish your bot to Azure](#publish-your-bot-to-azure) | + +### [Multi-tenant](#tab/multitenant) + +| Information | Where generated or found | Where used | +|:-|:-|:-| +| App type | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md), [Update project configuration settings](#update-project-configuration-settings) | +| App ID | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md) | +| App password | [Create an identity resource](#create-an-identity-resource) | [Use Azure CLI to create an App Service resource](provision-app-service.md), [Update project configuration settings](#update-project-configuration-settings) | +| Base app service URL | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Use Azure CLI to create or update an Azure Bot resource](provision-azure-bot.md) | +| App Service name | [Use Azure CLI to create an App Service resource](provision-app-service.md) | [Publish your bot to Azure](#publish-your-bot-to-azure) | + +--- + +## Sign in and select subscription + +[!INCLUDE [Sign into Azure and select a subscription](./includes/az-cli/sign-in-select-subscription.md)] + +## Create resource groups + +If you don't already have an appropriate resource group, use the `az group create` command to create the new resource groups you need. + +```azurecli +az group create --name "" --location "" +``` + +| Option | Description | +|:---------|:--------------------------------------------------| +| name | The name of the resource group to create. | +| location | The region in which to create the resource group. | + +For more information, see [How to manage Azure resource groups with the Azure CLI](/cli/azure/manage-azure-groups-azure-cli). + +## Create an identity resource + +### [User-assigned managed identity](#tab/userassigned) + +1. To create a user-assigned managed identity, use the `az identity create` command. + On success, the command generates JSON output. + + ```azurecli + az identity create --resource-group "" --name "" + ``` + + | Option | Description | + |:---------------|:----------------------------------------------------------------| + | resource-group | The name of the resource group in which to create the identity. | + | name | The name of the identity resource to create. | + + For more information, see the [az identity](/cli/azure/identity) reference. + +1. Record values you'll need in later steps. + 1. The resource group name for the identity resource + 1. The name of the identity resource + 1. The `clientId` from the command output + +### [Single-tenant](#tab/singletenant) + +Use the following commands to create your app registration and set its password. +On success, these commands generate JSON output. + +1. Use the `az ad app create` command to create an Microsoft Entra ID app registration. + This command generates an app ID that you'll use in the next step. + + ```azurecli + az ad app create --display-name "" --sign-in-audience "AzureADMyOrg" + ``` + + | Option | Description | + |:-----------------|:------------------------------------------------------------------------------------------| + | display-name | The display name for your app registration. | + | sign-in-audience | The supported Microsoft accounts for the app. Use `AzureADMyOrg` for a single tenant app. | + +1. Use the `az ad app credential reset` command to generate a new password for your app registration. + + ```azurecli + az ad app credential reset --id "" + ``` + +1. Record values you'll need in later steps: the _app ID_ and _password_ from the command output. + +For more information about `az ad app`, see the [command reference](/cli/azure/ad/app). For more information about the `sign-in-audience` parameter, see [sigInAudience values](/graph/api/resources/application#signinaudience-values). + +### [Multi-tenant](#tab/multitenant) + +[!INCLUDE [create-identity-multi-tenant](includes/az-cli/create-identity-multi-tenant.md)] + +--- + +## Create resources with ARM templates + +Create the App Service and the Azure Bot resources for your bot. +Both steps use an ARM template and the `az deployment group create` Azure CLI command to create the resource or resources. + +1. Create an App Service resource for your bot. The App service can be within a new or existing App Service Plan. + + For detailed steps, see [Use Azure CLI to create an App Service](./provision-app-service.md). + +1. Create an Azure Bot resource for your bot. + + For detailed steps, see [Use Azure CLI to create or update an Azure Bot](./provision-azure-bot.md). + +> [!IMPORTANT] +> You can do these steps in either order. +> However, if you create your Azure Bot first, you'll need to update its messaging endpoint after you create your App Service resource. + +## Update project configuration settings + +[!INCLUDE [app ID and password](includes/authentication/azure-bot-appid-password.md)] + +## Prepare your project files + +[!INCLUDE [Create Kudu files, compress files](includes/az-cli/prepare-for-deployment.md)] + +## Publish your bot to Azure + +[!INCLUDE [deploy code to Azure](includes/az-cli/deploy-to-azure.md)] + +## Test in Web Chat + +[!INCLUDE [test in web chat](includes/deploy/snippet-test-in-web-chat.md)] + +## Clean up resources + +If you're not going to publish this application, delete the associated resources with the following steps: + +1. In the Azure portal, open the resource group for your bot. + 1. Select **Delete resource group** to delete the group and all the resources it contains. + 1. Enter the _resource group name_ in the confirmation pane, then select **Delete**. +1. If you created a single-tenant or multi-tenant app: + 1. Go to the Microsoft Entra ID blade. + 1. Locate the app registration you used for your bot, and delete it. + +## Additional resources + +[!INCLUDE [Azure documentation for bot hosting](includes/azure-docs/apps-and-resources.md)] + +### Kudu files + +The web app deployment command uses Kudu to deploy C#, JavaScript, and Python bots. +When using the non-configured [zip deploy API](https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file-or-url) to deploy your bot's code, the behavior is as follows: + +_Kudu assumes by default that deployments from .zip files are ready to run and don't require extra build steps during deployment, such as npm install or dotnet restore/dotnet publish._ + +It's important to include your built code with all necessary dependencies in the zip file being deployed; otherwise, your bot won't work as intended. For more information, see the Azure documentation on how to [Deploy files to App Service](/azure/app-service/deploy-zip). + +## Next steps + +> [!div class="nextstepaction"] +> [Set up continuous deployment](bot-service-build-continuous-deployment.md) diff --git a/articles/provision-app-service.md b/articles/provision-app-service.md new file mode 100644 index 000000000..b1c321dc2 --- /dev/null +++ b/articles/provision-app-service.md @@ -0,0 +1,132 @@ +--- +title: Use Azure CLI to create an App Service resource +description: Learn how to create an App Service resource with the Azure CLI and an ARM template. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - devx-track-arm-template + - devx-track-azurecli + - evergreen +--- + +# Use Azure CLI to create an App Service resource + +[!INCLUDE [applies-to-v4](./includes/applies-to-v4-current.md)] + +This article describes how to create an App Service resource with the Azure CLI and an Azure Resource Manager template (ARM template) as part of the process to provision and publish a bot. The app service is sometimes referred to as a _web app_. + +> [!IMPORTANT] +> Python bots can't be deployed to a resource group that contains Windows services or bots. +> Multiple Python bots can be deployed to the same resource group; however, you need to create other services (such as Azure AI services) in another resource group. + +- For information on the complete process, see how to [Provision and publish a bot](provision-and-publish-a-bot.md). +- For information on how to create an Azure Bot resource, see [Use Azure CLI to create an Azure Bot resource](provision-azure-bot.md). + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] + +## Prerequisites + +[!INCLUDE [Azure CLI prerequisites](./includes/az-cli/prereqs.md)] + +- This process uses an Azure Resource Manager template (ARM template) to create an App Service resource for your bot. + + If you don't have the current templates, create a copy in your bot project of the **deploymentTemplates** folder: [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates/Microsoft.BotFramework.CSharp.EchoBot/content), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder/generators/app/templates/echo), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators/app/templates/echo/%7B%7Bcookiecutter.bot_name%7D%7D), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators/generators/app/templates/echo/project). + +> [!TIP] +> This is part of the larger process to provision and publish a bot. +> See how to [Provision and publish a bot](provision-and-publish-a-bot.md) for a complete list of prerequisites. + +## Edit parameters file + +Edit the parameters file for the ARM template to contain the values you want to use. + +> [!IMPORTANT] +> You must use the same `appType` value for your App Service and Azure Bot resources. + +If your project doesn't yet contain the most recent ARM template and parameters files, you can copy them from the Bot Framework SDK repo for your language: [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates/Microsoft.BotFramework.CSharp.EchoBot/content), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder/generators/app/templates/echo), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators/app/templates/echo/%7B%7Bcookiecutter.bot_name%7D%7D), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators/generators/app/templates/echo/project). + +This table describes the _deployment parameters_ in the parameters file, for use with the `parameters` command option. +By default, the name of the parameters file is **parameters-for-template-BotApp-with-rg.json**. + +| Parameter | Type | Description | +|:-|:-|:-| +| `appServiceName` | String | Required. The globally unique name of the app service. | +| `existingAppServicePlanName` | String | Optional. The name of an _existing_ app service plan with which to create the app service for the bot. | +| `existingAppServicePlanLocation` | String | Optional. The location of the _existing_ app service plan. | +| `newAppServicePlanName` | String | Optional. The name of the _new_ app service plan. | +| `newAppServicePlanLocation` | String | Optional. The location of the _new_ app service plan. | +| `newAppServicePlanSku` | Object | Optional. The SKU for the _new_ app service plan. Default is the S1 (Standard) service plan. | +| `appType` | String | Required. How the identities of your bot resources are managed. Allowed values: "MultiTenant", "SingleTenant", and "UserAssignedMSI". Default is "MultiTenant". | +| `appId` |String| Required. The client ID or app ID from the identity resource you created earlier. This is used as the Microsoft app ID of the app service. | +| `appSecret` |String| Optional. For single-tenant and multi-tenant app types, the password for the identity resource. | +| `UMSIName` | String | Optional. For user-assigned managed identity app types, the name of the identity resource.| +| `UMSIResourceGroupName` | String | Optional. For user-assigned managed identity app types, the resource group for the identity resource. | +| `tenantId` | String | Optional. For user-assigned managed identity and single-tenant app types, The Microsoft Entra ID tenant ID for the identity resource. | + +Not all parameters apply to all app types. + +### [User-assigned managed identity](#tab/userassigned) + +- Provide values for `UMSIName`, `UMSIResourceGroupName`, and `tenantId`. +- Leave `appSecret` blank. + +### [Single-tenant](#tab/singletenant) + +- Provide values for `appSecret` and `tenantId`. +- Leave `UMSIName` and `UMSIResourceGroupName` blank. + +### [Multi-tenant](#tab/multitenant) + +- Provide a value for `appSecret`. +- Leave `UMSIName`, `UMSIResourceGroupName`, and `tenantId` blank. + +--- + +Some parameters are specific to using an existing or new app service plan. + +### [Existing plan](#tab/existingplan) + +- Provide values for `existingAppServicePlanName` and `existingAppServicePlanLocation`. +- Leave `newAppServicePlanName`, `newAppServicePlanLocation`, and `newAppServicePlanSku` blank. + +### [New plan](#tab/newplan) + +- Provide values for `newAppServicePlanName`, `newAppServicePlanLocation`, and `newAppServicePlanSku`. +- Leave `existingAppServicePlanName` and `existingAppServicePlanLocation` blank. + +--- + +## Create the app service + +Create the app service for your bot. + +```azurecli +az deployment group create --resource-group --template-file --parameters "@" +``` + +| Option | Description | +|:---------------|:----------------------------------------------------------------------------------------------------| +| resource-group | Name of the Azure resource group in which to create the app service. | +| template-file | The path to the ARM template for the app service. The path can be relative or absolute. | +| parameters | The path to the parameters file to use with the ARM template. The path can be relative or absolute. | + +For projects created with the latest generators, the ARM template and parameter files are located in the _DeploymentTemplates\DeployUseExistResourceGroup_ folder within the project. +The default file names are **template-BotApp-with-rg.json** and **parameters-for-template-BotApp-with-rg.json**. + +> [!TIP] +> +> - The base URL for your app service is based on the app service name: `https:.azurewebsites.net`. +> - The messaging endpoint for your bot will be the base URL plus `/api/messages`, such as `https:.azurewebsites.net/api/messages`. + +## Additional information + +For more information about ARM templates, see [What are ARM templates?](/azure/azure-resource-manager/templates/overview) and [How to use Azure Resource Manager (ARM) deployment templates with Azure CLI](/azure/azure-resource-manager/templates/deploy-cli). + +## Next steps + +If you created the App Service as part of a bot deployment, see [Create resources with ARM templates](provision-and-publish-a-bot.md#create-resources-with-arm-templates) to continue the process. diff --git a/articles/provision-azure-bot.md b/articles/provision-azure-bot.md new file mode 100644 index 000000000..9bc37f13b --- /dev/null +++ b/articles/provision-azure-bot.md @@ -0,0 +1,124 @@ +--- +title: Use Azure CLI to create an Azure Bot resource +description: Learn how to create an Azure Bot resource with the Azure CLI and an ARM template. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - template-how-to + - devx-track-arm-template + - devx-track-azurecli + - evergreen +--- + +# Use Azure CLI to create or update an Azure Bot resource + +[!INCLUDE [applies-to-v4](./includes/applies-to-v4-current.md)] + +This article describes how to create or update an Azure Bot resource with the Azure CLI and an Azure Resource Manager template (ARM template). + +This is part of the larger process to provision and publish a bot. + +- For information on the complete process, see [Provision and publish a bot](provision-and-publish-a-bot.md). +- For information on how to create an App Service resource, see [Use Azure CLI to create an App Service resource](provision-app-service.md). +- For instructions for how to use the Azure portal, see the [Create an Azure Bot resource](v4sdk/abs-quickstart.md) quickstart. + +[!INCLUDE [java-python-sunset-alert](includes/java-python-sunset-alert.md)] + +## Prerequisites + +[!INCLUDE [Azure CLI prerequisites](./includes/az-cli/prereqs.md)] + +- This process uses an Azure Resource Manager template (ARM template) to create an Azure Bot resource for your bot. + + If you don't have the current templates, create a copy in your bot project of the **deploymentTemplates** folder: [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates/Microsoft.BotFramework.CSharp.EchoBot/content), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder/generators/app/templates/echo), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators/app/templates/echo/%7B%7Bcookiecutter.bot_name%7D%7D), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators/generators/app/templates/echo/project). + +> [!TIP] +> This is part of the larger process to provision and publish a bot. +> See how to [Provision and publish a bot](provision-and-publish-a-bot.md) for a complete list of prerequisites. + +## Edit parameters file + +Edit the parameters file for the ARM template to contain the values you want to use. + +> [!IMPORTANT] +> You must use the same `appType` and `appId` values when you create your App Service and Azure Bot resources. + +If your project doesn't yet contain the most recent ARM template and parameters files, you can copy them from the Bot Framework SDK repo for your language: [C#](https://github.com/microsoft/botbuilder-dotnet/tree/main/generators/dotnet-templates/Microsoft.BotFramework.CSharp.EchoBot/content), [JavaScript](https://github.com/microsoft/botbuilder-js/tree/main/generators/generator-botbuilder/generators/app/templates/echo), [Python](https://github.com/microsoft/botbuilder-python/tree/main/generators/app/templates/echo/%7B%7Bcookiecutter.bot_name%7D%7D), or [Java](https://github.com/microsoft/botbuilder-java/tree/main/generators/generators/app/templates/echo/project). + +This table describes the _deployment parameters_ in the parameters file, for use with the `parameters` command option. +By default, the name of the parameters file is **parameters-for-template-AzureBot-with-rg.json**. + +| Parameter | Type | Description | +|:-|:-|:-| +| `azureBotId` |String| Required. The globally unique and immutable handle for your bot, such as `ContosoCustomerService`. | +| `azureBotSku` | String | Optional. The SKU of the Azure Bot resource. Allowed values: "F0" (free) and "S1" (standard). Default is "S1". | +| `azureBotRegion` | String | Optional. The location of the Azure Bot. Allowed values: "global", "westeurope", "westus" and "centralindia". Default is "global". | +| `botEndpoint` |String| Optional. The messaging endpoint for your bot, such as `https://.azurewebsites.net/api/messages`. | +| `appType` | String | Required. How the identities of your bot resources are managed. Allowed values are: "MultiTenant", "SingleTenant", and "UserAssignedMSI". Default is "MultiTenant". | +| `appId` |String| Required. The client ID or app ID from the identity resource you created earlier. This is the Microsoft app ID of the app service. | +| `UMSIName` | String | Optional. For user-assigned managed identity app types, the name of the identity resource.| +| `UMSIResourceGroupName` | String | Optional. For user-assigned managed identity app types, the resource group for the identity resource. | +| `tenantId` | String | Optional. For user-assigned managed identity and single-tenant app types, The Microsoft Entra ID tenant ID for the identity resource. | + +> [!TIP] +> The bot's messaging endpoint must be set before a published bot can receive messages. + +Not all parameters apply to all app types. + +### [User-assigned managed identity](#tab/userassigned) + +Provide values for `UMSIName`, `UMSIResourceGroupName`, and `tenantId`. + +### [Single-tenant](#tab/singletenant) + +- Provide a value for `tenantId`. +- Leave `UMSIName` and `UMSIResourceGroupName` blank. + +### [Multi-tenant](#tab/multitenant) + +Leave `UMSIName`, `UMSIResourceGroupName`, and `tenantId` blank. + +--- + +## Create the Azure Bot resource + +To create the Azure Bot resource for your bot, use the following command. + +```azurecli +az deployment group create --resource-group --template-file --parameters "@" +``` + +| Option | Description | +|:---------------|:----------------------------------------------------------------------------------------------------| +| resource-group | Name of the Azure resource group in which to create the App Service. | +| template-file | The path to the ARM template for the App Service. The path can be relative or absolute. | +| parameters | The path to the parameters file to use with the ARM template. The path can be relative or absolute. | + +For projects created with the latest generators, the ARM template and parameter files are located in the _DeploymentTemplates\DeployUseExistResourceGroup_ folder within the project. +The default file names are **template-AzureBot-with-rg.json** and **parameters-for-template-AzureBot-with-rg.json**. + +## To update your Azure Bot resource + +To add or update the messaging endpoint for your Azure Bot, use the following command. + +```azurecli +az bot update --resource-group --name --endpoint +``` + +| Option | Description | +|:---------------|:--------------------------------------------------------------------------------------------------------| +| resource-group | Name of the Azure resource group that contains the App Service. | +| name | The globally unique and immutable handle for your bot. | +| endpoint | The messaging endpoint for your bot, such as `https://.azurewebsites.net/api/messages`. | + +## Additional information + +For more information about ARM templates, see [What are ARM templates?](/azure/azure-resource-manager/templates/overview) and [How to use Azure Resource Manager (ARM) deployment templates with Azure CLI](/azure/azure-resource-manager/templates/deploy-cli). + +## Next steps + +If you created the App Service as part of a bot deployment, see [Create resources with ARM templates](provision-and-publish-a-bot.md#create-resources-with-arm-templates) to continue the process. diff --git a/articles/python/bot-builder-python-quickstart.md b/articles/python/bot-builder-python-quickstart.md deleted file mode 100644 index 60db6e56e..000000000 --- a/articles/python/bot-builder-python-quickstart.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Create a bot using Bot Framework SDK for Python | Microsoft Docs -description: Quickly create a bot using the Bot Framework SDK for Python. -keywords: quickstart, bot framework sdk, getting started -author: emgrol -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/10/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Create a bot with the Bot Framework SDK for Python - -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] - -This quickstart walks you through building a bot by using the Python Echo Bot template, and then testing it with the Bot Framework Emulator. - -## Prerequisites -- Python [3.6](https://www.python.org/downloads/release/python-369/) or [3.7](https://www.python.org/downloads/release/python-375/) -- [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) -- [git](https://git-scm.com/) -- knowledge of asynchronous programming in Python - -## Create a bot -1. Open a terminal and navigate to the folder where you're saving your bot locally. Install the necessary packages by running the following commands: -- `pip install botbuilder-core` -- `pip install asyncio` -- `pip install cookiecutter` - -The last package, cookiecutter, will be used to generate your bot. Verify that cookiecutter was installed correctly by running `cookiecutter --help`. - -2. To create your bot run: - -```cmd -cookiecutter https://github.com/microsoft/botbuilder-python/releases/download/Templates/echo.zip -``` - -This command creates an Echo Bot based on the Python [echo template](https://github.com/microsoft/botbuilder-python/tree/master/generators/app/templates/echo). - -3. You will then be prompted for the *name* of the bot and a *description*. Name your bot `echo-bot` and set the description to `A bot that echoes back user response.` as shown below: - -![set name and description](../media/python/quickstart/set-name-description.png) - -Copy the last four digits in the address on the last line (usually 3978) since you will be using them in the next step. You are now ready to start your bot. - -## Start you bot -1. From a terminal navigate to the `echo-bot` folder where you saved your bot. Run `pip install -r requirements.txt` to install any required packages to run your bot. - -2. Once the packages are installed run `python app.py` to start your bot. You will know your bot is ready to test when you see the last line shown in the screenshot below: - -![bot running locally](../media/python/quickstart/bot-running-locally.png) - -## Start the Emulator and connect your bot -1. Start the Emulator and click the **Open Bot** button. - -2. After clicking the button a box window will open where you set the necessary values to run the bot. Use the number you saved earlier and set the **Bot URL** to `http://localhost:/api/messages` as seen below: - -![open a bot screen](../media/python/quickstart/open-bot.png) - -3. Click the **Connect** button and your bot should start. Try testing the bot by typing anything and clicking *Enter* as seen below: - -![connect and test](../media/python/quickstart/connect-and-start.png) - -## Additional resources -See [tunneling (ngrok)](https://github.com/Microsoft/BotFramework-Emulator/wiki/Tunneling-(ngrok)) for how to connect to a bot hosted remotely. - -## Next steps - -> [!div class="nextstepaction"] -> [Deploy your bot to Azure](../bot-builder-deploy-az-cli.md) - diff --git a/articles/python/bot-builder-python-samples.md b/articles/python/bot-builder-python-samples.md deleted file mode 100644 index 3de86f38c..000000000 --- a/articles/python/bot-builder-python-samples.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: Sample bots for Bot Framework SDK for Python - Bot Service -description: Explore sample bots that can help kickstart your bot development with the Bot Framework SDK for Python. -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Python samples for Bot Framework SDK -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] - -These samples demonstrate task-focused bots that show how to take advantage of features in the Bot Framework SDK for Python. The SDK v4 is in preview, visit Python [GitHub repo](https://github.com/Microsoft/botbuilder-python) for more information. - -To get the samples, clone the [botbuilder-python](https://github.com/Microsoft/botbuilder-python) GitHub repository using Git. - -```cmd -git clone https://github.com/Microsoft/botbuilder-python.git -cd samples -``` -The sample bots built with the Bot Framework SDK for Python are organized in the **samples** directory. diff --git a/articles/ref-oauth-redirect-urls.md b/articles/ref-oauth-redirect-urls.md new file mode 100644 index 000000000..2d1fd0e71 --- /dev/null +++ b/articles/ref-oauth-redirect-urls.md @@ -0,0 +1,65 @@ +--- +title: Supported OAuth URLs +description: Azure AI Bot Service provides various OAuth URLs. Choose a URL based on data residency requirements and which cloud your bot is in. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +# OAuth URL support in Azure AI Bot Service + +Azure AI Bot Service provides different OAuth and OAuth redirect URLs to meet specific needs. + +- The bot needs the OAuth URL at run time. +- You need to provide the OAuth redirect URL when you create or configure your OAuth identity provider. +- For more information, see [how to add authentication to your bot](v4sdk/bot-builder-authentication.md). + +Choose the URLs to use with your bot and identity provider based on your data residency requirements and whether your bot is in the public cloud or the Microsoft Azure Government cloud or the Microsoft Azure operated by 21Vianet. + +| Data residency | Cloud | OAuth URL | OAuth Redirect URL | +|:---------------|:-----------------|:----------------------------------------------|:-----------------------------------------------------------------| +| None | Public | `https://token.botframework.com` | `https://token.botframework.com/.auth/web/redirect` | +| Europe | Public | `https://europe.token.botframework.com` | `https://europe.token.botframework.com/.auth/web/redirect` | +| United States | Public | `https://unitedstates.token.botframework.com` | `https://unitedstates.token.botframework.com/.auth/web/redirect` | +| India | Public | `https://india.token.botframework.com` | `https://india.token.botframework.com/.auth/web/redirect` | +| None | Azure Government | `https://token.botframework.azure.us` | `https://token.botframework.azure.us/.auth/web/redirect` | +| None | Azure operated by 21Vianet | `https://token.botframework.azure.cn` | `https://token.botframework.azure.cn/.auth/web/redirect` | + +The default OAuth and OAuth redirect URLs are `https://token.botframework.com` and `https://token.botframework.com/.auth/web/redirect`, which can be used for public-cloud bots with no data residency requirements. + +## To configure OAuthUrl in bot +Update appsettings.json to include OAuthUrl options: + +```json +{ + "MicrosoftAppType": "", + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + + "BotOpenIdMetadata": "https://login.botframework.com/v1/.well-known/openidconfiguration", + "CallerId": "urn:botframework:azure", + "OAuthUrl": "https://europe.token.botframework.com/", + "ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.com/v1/.well-known/openidconfiguration", + "ToBotFromChannelTokenIssuer": "https://api.botframework.com", + "ToBotFromEmulatorOpenIdMetadataUrl": "https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration", + "ToChannelFromBotLoginUrl": "https://login.microsoftonline.com/{0}", + "ToChannelFromBotOAuthScope": "https://api.botframework.com", + "ValidateAuthority": true +} +``` + +## Additional information + +Some environments use endpoints different than the ones listed here. + +See these articles for related information. + +- [Regionalization in Azure AI Bot Service](v4sdk/bot-builder-concept-regionalization.md) +- [Bot Framework authentication basics](v4sdk/bot-builder-authentication-basics.md) +- [Configure Bot Framework bots for US Government customers](how-to-deploy-gov-cloud-high.md) +- [Configure a bot in Microsoft Azure operated by 21Vianet](how-to-deploy-china-cloud.md) diff --git a/articles/resources/TOC.md b/articles/resources/TOC.md deleted file mode 100644 index 61b9362b4..000000000 --- a/articles/resources/TOC.md +++ /dev/null @@ -1,24 +0,0 @@ -# Virtual Assistant -## [Overview](../v4sdk/bot-builder-virtual-assistant-introduction.md) -## [Template Introduction](../v4sdk/bot-builder-virtual-assistant-template.md) -# Skills -## [Overview](../v4sdk/bot-builder-skills-overview.md) -# WebChat -## [Overview](../v4sdk/bot-builder-webchat-overview.md) -## [Customization](../v4sdk/bot-builder-webchat-customization.md) -# [FAQ](../bot-service-resources-bot-framework-faq.md) -# [Get support](../bot-service-resources-links-help.md) -# [Channel reference](../bot-service-channels-reference.md) -# [Guide to identifiers](../bot-service-resources-identifiers-guide.md) -# [App Insights keys](../bot-service-resources-app-insights-keys.md) -# [User-agent requests](../bot-service-resources-user-agent.md) -# [Bot review guidelines](../bot-service-review-guidelines.md) -# [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) -# [Bot Framework Card schema](https://aka.ms/botSpecs-cardSchema) -# [Bot Framework Transcript schema](https://aka.ms/botSpecs-transcripts) -# [Bot Service Compliance](../v4sdk/bot-service-compliance.md) -# Troubleshoot -## [Troubleshoot general problems](../bot-service-troubleshoot-general-problems.md) -## [Troubleshoot bot configuration issues](../bot-service-troubleshoot-bot-configuration.md) -## [Troubleshoot HTTP 500 errors](../bot-service-troubleshoot-500-errors.md) -## [Troubleshoot authentication](../bot-service-troubleshoot-authentication-problems.md) diff --git a/articles/rest-api/TOC.md b/articles/rest-api/TOC.md deleted file mode 100644 index 62ad6f5a7..000000000 --- a/articles/rest-api/TOC.md +++ /dev/null @@ -1,35 +0,0 @@ -# Bot Framework REST API -## [Overview](bot-framework-rest-overview.md) -## [Key concepts](bot-framework-rest-connector-concepts.md) -## [Create a bot with REST](~/rest-api/bot-framework-rest-connector-quickstart.md) -## [API reference](bot-framework-rest-connector-api-reference.md) -# Connector -## [Authentication](bot-framework-rest-connector-authentication.md) -## [Activities overview](https://aka.ms/botSpecs-activitySchema) -## [Create messages](bot-framework-rest-connector-create-messages.md) -## [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) -## [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) -## [Add rich cards to messages](bot-framework-rest-connector-add-rich-cards.md) -## [Add speech to messages](bot-framework-rest-connector-text-to-speech.md) -## [Add input hints to messages](bot-framework-rest-connector-add-input-hints.md) -## [Add suggested actions to messages](bot-framework-rest-connector-add-suggested-actions.md) -## [Implement channel-specific functionality](bot-framework-rest-connector-channeldata.md) -## [Manage state data](bot-framework-rest-state.md) -# Direct Line API 3.0 -## [Key concepts](bot-framework-rest-direct-line-3-0-concepts.md) -## [Authentication](bot-framework-rest-direct-line-3-0-authentication.md) -## [Start a conversation](bot-framework-rest-direct-line-3-0-start-conversation.md) -## [Reconnect to a conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) -## [Send an activity to the bot](bot-framework-rest-direct-line-3-0-send-activity.md) -## [Receive activities from the bot](bot-framework-rest-direct-line-3-0-receive-activities.md) -## [End a conversation](bot-framework-rest-direct-line-3-0-end-conversation.md) -## [API reference](bot-framework-rest-direct-line-3-0-api-reference.md) -## [Swagger file](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-protocol/directline-3.0.json) -# Direct Line API 1.1 -## [Key concepts](bot-framework-rest-direct-line-1-1-concepts.md) -## [Authentication](bot-framework-rest-direct-line-1-1-authentication.md) -## [Start a conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) -## [Send a message to the bot](bot-framework-rest-direct-line-1-1-send-message.md) -## [Receive messages from the bot](bot-framework-rest-direct-line-1-1-receive-messages.md) -## [API reference](bot-framework-rest-direct-line-1-1-api-reference.md) -## [Swagger file](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-protocol/directline-1.1.json) diff --git a/articles/rest-api/bot-framework-rest-connector-add-input-hints.md b/articles/rest-api/bot-framework-rest-connector-add-input-hints.md index e52418c44..589ffb040 100644 --- a/articles/rest-api/bot-framework-rest-connector-add-input-hints.md +++ b/articles/rest-api/bot-framework-rest-connector-add-input-hints.md @@ -1,30 +1,28 @@ --- title: Add input hints to messages - Bot Service -description: Learn how to add input hints to messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to add input hints to bot messages. See how to use the Bot Framework Connector service to specify whether a bot is accepting, expecting, or ignoring user input. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Add input hints to messages -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-input-hints.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-input-hints.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-input-hints.md) +# Add input hints to messages with the Bot Connector API -By specifying an input hint for a message, you can indicate whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. For many channels, this enables clients to set the state of user input controls accordingly. For example, if a message's input hint indicates that the bot is ignoring user input, the client may close the microphone and disable the input box to prevent the user from providing input. +By specifying an input hint for a message, you can indicate whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. For channels that support this field, this enables clients to set the state of user input controls accordingly. For example, if a message's input hint indicates that the bot is ignoring user input, the client may close the microphone and disable the input box to prevent the user from providing input. ## Accepting input -To indicate that your bot is passively ready for input but is not awaiting a response from the user, set the `inputHint` property to **acceptingInput** within the [Activity][] object that represents your message. On many channels, this will cause the client's input box to be enabled and microphone to be closed, but still accessible to the user. For example, Cortana will open the microphone to accept input from the user if the user holds down the microphone button. +To indicate that your bot is passively ready for input but isn't awaiting a response from the user, set the `inputHint` property to **acceptingInput** within the [Activity][] object that represents your message. On many channels, this will cause the client's input box to be enabled and microphone to be closed, but still accessible to the user. -The following example shows a request that sends a message and specifies that the bot is accepting input. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a request that sends a message and specifies that the bot is accepting input. In this example request, Direct Line represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -52,12 +50,12 @@ Content-Type: application/json ## Expecting input -To indicate that your bot is awaiting a response from the user, set the `inputHint` property to **expectingInput** within the [Activity][] object that represents your message. On many channels, this will cause the client's input box to be enabled and microphone to be open. +To indicate that your bot is actively awaiting a response from the user, set the `inputHint` property to **expectingInput** within the [Activity][] object that represents your message. On channels that support it, this will cause the client's input box to be enabled and microphone to be open. -The following example shows a request that sends a message and specifies that the bot is expecting input. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a request that sends a message and specifies that the bot is expecting input. In this example request, Direct Line represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -84,13 +82,13 @@ Content-Type: application/json ``` ## Ignoring input - -To indicate that your bot is not ready to receive input from the user, set the `inputHint` property to **ignoringInput** within the [Activity][] object that represents your message. On many channels, this will cause the client's input box to be disabled and microphone to be closed. -The following example shows a request that sends a message and specifies that the bot is ignoring input. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +To indicate that your bot isn't ready to receive input from the user, set the `inputHint` property to **ignoringInput** within the [Activity][] object that represents your message. On channels that support it, this will cause the client's input box to be disabled and microphone to be closed. + +The following example shows a request that sends a message and specifies that the bot is ignoring input. In this example request, Direct Line represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` diff --git a/articles/rest-api/bot-framework-rest-connector-add-media-attachments.md b/articles/rest-api/bot-framework-rest-connector-add-media-attachments.md index bd681b5b5..1c1c46e50 100644 --- a/articles/rest-api/bot-framework-rest-connector-add-media-attachments.md +++ b/articles/rest-api/bot-framework-rest-connector-add-media-attachments.md @@ -1,32 +1,30 @@ --- -title: Add media attachments to messages - Bot Service +title: Add media attachments to messages in Bot Framework SDK description: Learn how to add media attachments to messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 10/25/2018 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - evergreen --- -# Add media attachments to messages -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-media-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-receive-attachments.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-media-attachments.md) +# Add media attachments to messages with the Bot Connector API -Bots and channels typically exchange text strings but some channels also support exchanging attachments, which lets your bot send richer messages to users. For example, your bot can send media attachments (e.g., images, videos, audio, files) and [rich cards](bot-framework-rest-connector-add-rich-cards.md). This article describes how to add media attachments to messages using the Bot Connector service. +Bots and channels typically exchange text strings but some channels also support exchanging attachments, which lets your bot send richer messages to users. For example, your bot can send media attachments (such as images, videos, audio, files) and [rich cards](bot-framework-rest-connector-add-rich-cards.md). This article describes how to add media attachments to messages using the Bot Connector service. [!INCLUDE [Channel Inspector intro](~/includes/snippet-channel-inspector.md)] ## Add a media attachment -To add a media attachment to a message, create an [Attachment][] object, set the `name` property, set the `contentUrl` property to the URL of the media file, and set the `contentType` property to the appropriate media type (e.g., **image/png**, **audio/wav**, **video/mp4**). Then within the [Activity][] object that represents your message, specify your `Attachment` object within the `attachments` array. +To add a media attachment to a message, create an [Attachment][] object, set the `name` property, set the `contentUrl` property to the URL of the media file, and set the `contentType` property to the appropriate media type (such as **image/jpg**, **audio/wav**, **video/mp4**). Then within the [Activity][] object that represents your message, specify your `Attachment` object within the `attachments` array. -The following example shows a request that sends a message containing text and a single image attachment. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a request that sends a message containing text and a single image attachment. In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -49,8 +47,8 @@ Content-Type: application/json "text": "Here's a picture of the duck I was telling you about.", "attachments": [ { - "contentType": "image/png", - "contentUrl": "https://aka.ms/DuckOnARock", + "contentType": "image/jpg", + "contentUrl": "https://www.publicdomainpictures.net/pictures/30000/t2/duck-on-a-rock.jpg", "name": "duck-on-a-rock.jpg" } ], @@ -58,7 +56,7 @@ Content-Type: application/json } ``` -For channels that support inline binaries of an image, you can set the `contentUrl` property of the `Attachment` to a base64 binary of the image (for example, **data:image/png;base64,iVBORw0KGgo…**). The channel will display the image or the image's URL next to the message's text string. +For channels that support inline binaries of an image, you can set the `contentUrl` property of the `Attachment` to a base64 binary of the image (for example, **data:image/jpg;base64,iVBORw0KGgo...**). The channel will display the image or the image's URL next to the message's text string. ```json { @@ -78,8 +76,8 @@ For channels that support inline binaries of an image, you can set the `contentU "text": "Here's a picture of the duck I was telling you about.", "attachments": [ { - "contentType": "image/png", - "contentUrl": "data:image/png;base64,iVBORw0KGgo…", + "contentType": "image/jpg", + "contentUrl": "data:image/jpg;base64,iVBORw0KGgo...", "name": "duck-on-a-rock.jpg" } ], @@ -89,9 +87,9 @@ For channels that support inline binaries of an image, you can set the `contentU You can attach a video file or audio file to a message by using the same process as described above for an image file. Depending on the channel, the video and audio may be played inline or it may be displayed as a link. -> [!NOTE] +> [!NOTE] > Your bot may also receive messages that contain media attachments. -> For example, a message that your bot receives may contain an attachment +> For example, a message that your bot receives may contain an attachment > if the channel enables the user to upload a photo to be analyzed or a document to be stored. ## Add an AudioCard attachment @@ -138,7 +136,7 @@ Adding an [AudioCard][] or [VideoCard][] attachment is the same as adding a medi } ``` -Once the channel receives this attachment, it will start playing the audio file. If a user interacts with audio by clicking the **Pause** button, for example, the channel will send a callback to the bot with a JSON that look something like this: +Once the channel receives this attachment, it will start playing the audio file. If a user interacts with audio by selecting **Pause**, for example, the channel will send a callback to the bot with a JSON that looks something like this: ```json { @@ -170,8 +168,8 @@ The media event name **media/pause** will appear in the `activity.name` field. R - [Create messages](bot-framework-rest-connector-create-messages.md) - [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) - [Add rich cards to messages](bot-framework-rest-connector-add-rich-cards.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) -- [Bot Framework card schema](https://aka.ms/botSpecs-cardSchema) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) +- [Bot Framework card schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md) [Activity]: bot-framework-rest-connector-api-reference.md#activity-object [Attachment]: bot-framework-rest-connector-api-reference.md#attachment-object diff --git a/articles/rest-api/bot-framework-rest-connector-add-rich-cards.md b/articles/rest-api/bot-framework-rest-connector-add-rich-cards.md index 9114cf043..4850ac7ad 100644 --- a/articles/rest-api/bot-framework-rest-connector-add-rich-cards.md +++ b/articles/rest-api/bot-framework-rest-connector-add-rich-cards.md @@ -1,26 +1,22 @@ --- title: Add rich card attachments to messages - Bot Service -description: Learn how to add rich cards to messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Find out how to add images, videos, audio, and files to bot messages. Learn about different types of rich cards and events that bots can process in cards. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Add rich card attachments to messages +# Adding rich card attachments to messages with the Bot Connector API -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-rich-card-attachments.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-rich-cards.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-rich-cards.md) +Some channels allow your bot to send _rich cards_ to users. A rich card is an attachment that contains interactive elements, such as buttons, text, images, audio, video, and so on. -Bots and channels typically exchange text strings but some channels also support exchanging attachments, which lets your bot send richer messages to users. For example, your bot can send rich cards and media attachments (e.g., images, videos, audio, files). This article describes how to add rich card attachments to messages using the Bot Connector service. - -> [!NOTE] -> For information about how to add media attachments to messages, see -> [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md). +This article describes how to create rich cards that you can attach to a message. +See how to [add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) for specific instructions on adding an attachment to a message. ## Types of rich cards @@ -61,38 +57,32 @@ To process events within rich cards, use [CardAction][] objects to specify what > See [Add an Adaptive Card to a message](#add-an-adaptive-card-to-a-message) for an example that shows how to > add buttons to an Adaptive Card. -This table lists the valid values for the `type` property of a `CardAction` object and describes the expected contents of the `value` property for each type: - -| type | value | -|----|----| -| openUrl | URL to be opened in the built-in browser | -| imBack | Text of the message to send to the bot (from the user who clicked the button or tapped the card). This message (from user to bot) will be visible to all conversation participants via the client application that is hosting the conversation. | -| postBack | Text of the message to send to the bot (from the user who clicked the button or tapped the card). Some client applications may display this text in the message feed, where it will be visible to all conversation participants. | -| call | Destination for a phone call in this format: **tel:123123123123** | -| playAudio | URL of audio to be played | -| playVideo | URL of video to be played | -| showImage | URL of image to be displayed | -| downloadFile | URL of file to be downloaded | -| signin | URL of OAuth flow to be initiated | +[!INCLUDE [Table of card action types](../includes/snippet-card-action-types.md)] ## Add a Hero card to a message -To add a rich card attachment to a message, first create an object that corresponds to the [type of card](#types-of-rich-cards) that you want to add to the message. Then create an [Attachment][] object, set its `contentType` property to the card's media type and its `content` property to the object you created to represent the card. Specify your `Attachment` object within the `attachments` array of the message. +To add a rich card attachment to a message: + +1. Create an object that represents the [type of card](#types-of-rich-cards) that you want to add to the message. +1. Create an [Attachment][] object: + - Set its `contentType` property to the card's media type. + - Set its `content` property to the object you created to represent the card. +1. Add the `Attachment` object to the `attachments` array of the message. > [!TIP] -> Messages that contain rich card attachments typically do not specify `text`. +> Messages that contain rich card attachments typically don't specify `text`. Some channels allow you to add multiple rich cards to the `attachments` array within a message. This capability can be useful in scenarios where you want to provide the user with multiple options. For example, if your bot lets users book hotel rooms, it could present the user with a list of rich cards that shows the types of available rooms. Each card could contain a picture and list of amenities corresponding to the room type and the user could select a room type by tapping a card or clicking a button. > [!TIP] > To display multiple rich cards in list format, set the [Activity][] object's `attachmentLayout` property to "list". > To display multiple rich cards in carousel format, set the `Activity` object's `attachmentLayout` property to "carousel". -> If the channel does not support carousel format, it will display the rich cards in list format, even if the `attachmentLayout` property specifies "carousel". +> If the channel doesn't support carousel format, it will display the rich cards in list format, even if the `attachmentLayout` property specifies "carousel". -The following example shows a request that sends a message containing a single Hero card attachment. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a complete message that contains a single Hero card attachment. In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -121,7 +111,7 @@ Content-Type: application/json "text": "descriptive text goes here", "images": [ { - "url": "https://aka.ms/DuckOnARock", + "url": "https://www.publicdomainpictures.net/pictures/30000/t2/duck-on-a-rock.jpg", "alt": "picture of a duck", "tap": { "type": "playAudio", @@ -138,7 +128,7 @@ Content-Type: application/json { "type": "openUrl", "title": "Watch Video", - "image": "https://aka.ms/DuckOnARock", + "image": "https://www.publicdomainpictures.net/pictures/30000/t2/duck-on-a-rock.jpg", "value": "url goes here of the duck in flight" } ] @@ -152,9 +142,11 @@ Content-Type: application/json ## Add an Adaptive card to a message The Adaptive Card can contain any combination of text, speech, images, buttons, and input fields. -Adaptive Cards are created using the JSON format specified in [Adaptive Cards](http://adaptivecards.io), which gives you full control over card content and format. +Adaptive Cards are created using the JSON format specified in [Adaptive Cards](https://adaptivecards.io), which gives you full control over card content and format. + +Leverage the information within the [Adaptive Cards](https://adaptivecards.io) site to understand Adaptive Card schema, explore Adaptive Card elements, and see JSON samples that can be used to create cards of varying composition and complexity. Additionally, you can use the Interactive Visualizer to design Adaptive Card payloads and preview card output. -Leverage the information within the [Adaptive Cards](http://adaptivecards.io) site to understand Adaptive Card schema, explore Adaptive Card elements, and see JSON samples that can be used to create cards of varying composition and complexity. Additionally, you can use the Interactive Visualizer to design Adaptive Card payloads and preview card output. The following example is a single Adaptive Card for a work assignment. +The following example shows a single Adaptive Card attachment object, representing a work assignment. To send this to a user, you would need to add it as an attachment in a message. ```json { @@ -273,17 +265,18 @@ Leverage the information within the [Adaptive Cards](http://adaptivecards.io) si ``` -The resulting card contains a title, information about who created the card (their name and avatar), when the card was created, a description of the work assignments, and information related to the assignment. There are also buttons which can be clicked to either comment on the work assignment or view it: +The resulting card contains a title, information about who created the card (their name and avatar), when the card was created, a description of the work assignments, and information related to the assignment. There are also buttons that can be clicked to either comment on the work assignment or view it: -![Adaptive Card calendar reminder](../media/adaptive-card-reminder.png) +:::image type="content" source="../media/adaptive-card-reminder.png" alt-text="Example calendar reminder as an Adaptive Card."::: ## Additional resources - [Create messages](bot-framework-rest-connector-create-messages.md) - [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) - [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) -- [Channel Inspector][ChannelInspector] +- [Channel reference][ChannelInspector] +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) +- [Bot Framework Cards schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-cards.md) [ChannelInspector]: ../bot-service-channels-reference.md [Activity]: bot-framework-rest-connector-api-reference.md#activity-object diff --git a/articles/rest-api/bot-framework-rest-connector-add-suggested-actions.md b/articles/rest-api/bot-framework-rest-connector-add-suggested-actions.md index d411d53aa..6f3c3d217 100644 --- a/articles/rest-api/bot-framework-rest-connector-add-suggested-actions.md +++ b/articles/rest-api/bot-framework-rest-connector-add-suggested-actions.md @@ -1,30 +1,30 @@ --- -title: Add suggested actions to messages - Bot Service +title: Add suggested actions to messages in Bot Framework SDK description: Learn how to add suggested actions to messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Add suggested actions to messages -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-add-suggested-actions.md) -> - [Node.js](../nodejs/bot-builder-nodejs-send-suggested-actions.md) -> - [REST](../rest-api/bot-framework-rest-connector-add-suggested-actions.md) +# Add suggested actions to messages with the Bot Connector API -[!INCLUDE [Introduction to suggested actions](../includes/snippet-suggested-actions-intro.md)] +Suggested actions enable your bot to present buttons that the user can tap to provide input. +Suggested actions appear close to the composer and enhance user experience by enabling the user to answer a question or make a selection with a simple tap of a button, rather than having to type a response with a keyboard. +Unlike buttons that appear within rich cards (which remain visible and accessible to the user even after being tapped), buttons that appear within the suggested actions pane will disappear after the user makes a selection. This prevents the user from tapping stale buttons within a conversation and simplifies bot development. ## Send suggested actions -To add suggested actions to a message, set the `suggestedActions` property of the [Activity][] object to specify the list of [CardAction][] objects that represent the buttons to be presented to the user. +To add suggested actions to a message, set the `suggestedActions` property of the [Activity][] object to specify the list of [CardAction][] objects that represent the buttons to be presented to the user. -The following request sends a message that presents three suggested actions to the user. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following request sends a message that presents three suggested actions to the user. In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` diff --git a/articles/rest-api/bot-framework-rest-connector-api-reference.md b/articles/rest-api/bot-framework-rest-connector-api-reference.md index d8f48feaf..766ecf6b8 100644 --- a/articles/rest-api/bot-framework-rest-connector-api-reference.md +++ b/articles/rest-api/bot-framework-rest-connector-api-reference.md @@ -1,19 +1,21 @@ --- -title: API reference - Bot Service -description: Learn about headers, operations, objects, and errors in the Bot Connector service and Bot State service. -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 08/02/2019 - +title: Bot Framework Connector service REST API reference +description: Learn about headers, operations, objects, and errors in the Bot Framework Connector service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - abs-meta-21q1 + - evergreen --- -# API reference +# API reference for the Bot Framework Connector service > [!NOTE] -> The REST API is not equivalent to the SDK. The REST API is provided to allow standard REST communication, however the preferred method of interacting with the Bot Framework is the SDK. +> The REST API isn't equivalent to the SDK. The REST API is provided to allow standard REST communication, however the preferred method of interacting with the Bot Framework is the SDK. Within the Bot Framework, the Bot Connector service enables your bot to exchange messages with users on channels that are configured in the Bot Framework Portal. The service uses industry-standard REST and JSON over HTTPS. @@ -21,6 +23,8 @@ Within the Bot Framework, the Bot Connector service enables your bot to exchange When a user sends a message to your bot, the incoming request contains an [Activity](#activity-object) object with a `serviceUrl` property that specifies the endpoint to which your bot should send its response. To access the Bot Connector service, use the `serviceUrl` value as the base URI for API requests. +When you don't already have a service URL for the channel, use `https://smba.trafficmanager.net/teams/` as the service URL. For more information, see [how to create a conversation and a proactive message in Teams](/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages#create-the-conversation). + For example, assume that your bot receives the following activity when the user sends a message to the bot. ```json @@ -28,7 +32,7 @@ For example, assume that your bot receives the following activity when the user "type": "message", "id": "bf3cc9a2f5de...", "timestamp": "2016-10-19T20:17:52.2891902Z", - "serviceUrl": "https://smba.trafficmanager.net/apis", + "serviceUrl": "https://smba.trafficmanager.net/teams/", "channelId": "channel's name/id", "from": { "id": "1234abcd", @@ -46,12 +50,12 @@ For example, assume that your bot receives the following activity when the user } ``` -The `serviceUrl` property within the user's message indicates that the bot should send its response to the endpoint `https://smba.trafficmanager.net/apis`; this will be the base URI for any subsequent requests that the bot issues in the context of this conversation. If your bot will need to send a proactive message to the user, be sure to save the value of `serviceUrl`. +The `serviceUrl` property within the user's message indicates that the bot should send its response to the endpoint `https://smba.trafficmanager.net/teams/`. The service URL will be the base URI for any subsequent requests that the bot issues in the context of this conversation. If your bot will need to send a proactive message to the user, be sure to save the value of `serviceUrl`. The following example shows the request that the bot issues to respond to the user's message. ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/bf3cc9a2f5de... +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/bf3cc9a2f5de... Authorization: Bearer eyJhbGciOiJIUzI1Ni... Content-Type: application/json ``` @@ -90,24 +94,29 @@ For details about how to obtain an access token for your bot, see [Authenticate ### Response headers -In addition to the standard HTTP response headers, every response will contain an `X-Correlating-OperationId` header. The value of this header is an ID that corresponds to the Bot Framework log entry which contains details about the request. Any time that you receive an error response, you should capture the value of this header. If you are not able to independently resolve the issue, include this value in the information that you provide to the Support team when you report the issue. +In addition to the standard HTTP response headers, every response will contain an `X-Correlating-OperationId` header. The value of this header is an ID that corresponds to the Bot Framework log entry, which contains details about the request. When you receive an error response, you should capture the value of this header. If you're not able to independently resolve the issue, include this value in the information that you provide to the Support team when you report the issue. ## HTTP status codes -The HTTP status code that is returned with each response indicates the outcome of the corresponding request. +The [HTTP status code](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) that is returned with each response indicates the outcome of the corresponding request. -| HTTP status code | Meaning | -|----|----| -| 200 | The request succeeded. | -| 201 | The request succeeded. | -| 202 | The request has been accepted for processing. | -| 204 | The request succeeded but no content was returned. | -| 400 | The request was malformed or otherwise incorrect. | -| 401 | The bot is not authorized to make the request. | -| 403 | The bot is not allowed to perform the requested operation. | -| 404 | The requested resource was not found. | -| 500 | An internal server error occurred. | -| 503 | The service is unavailable. | +> [!NOTE] +> The following table describes the most common HTTP status codes. +> Some errors are generated by the channel. For more information, you may need to read the channel's developer documentation. + +| HTTP status code | Meaning | +|:-----------------|:-------------------------------------------------------------| +| 200 | The request succeeded. | +| 201 | The request succeeded. | +| 202 | The request was accepted for processing. | +| 204 | The request succeeded but no content was returned. | +| 400 | The request was malformed or otherwise incorrect. | +| 401 | The bot isn't yet authenticated. | +| 403 | The bot isn't authorized to perform the requested operation. | +| 404 | The requested resource wasn't found. | +| 405 | The channel doesn't support the requested operation. | +| 500 | An internal server error occurred. | +| 503 | The service is temporarily unavailable. | ### Errors @@ -117,22 +126,28 @@ Any response that specifies an HTTP status code in the 4xx range or 5xx range wi Use these operations to create conversations, send messages (activities), and manage the contents of conversations. +> [!IMPORTANT] +> Not all channels support all endpoints. However, all channels should support the _reply to activity_ endpoint. +> +> For example, only Direct Line and Web Chat support the _get conversations_ endpoint. + | Operation | Description | |----|----| | [Create Conversation](#create-conversation) | Creates a new conversation. | -| [Send to Conversation](#send-to-conversation) | Sends an activity (message) to the end of the specified conversation. | -| [Reply to Activity](#reply-to-activity) | Sends an activity (message) to the specified conversation, as a reply to the specified activity. | -| [Get Conversations](#get-conversations) | Gets a list of conversations the bot has participated in. | -| [Get Conversation Members](#get-conversation-members) | Gets the members of the specified conversation. | -| [Get Conversation Paged Members](#get-conversation-paged-members) | Gets the members of the specified conversation one page at a time. | -| [Get Activity Members](#get-activity-members) | Gets the members of the specified activity within the specified conversation. | -| [Update Activity](#update-activity) | Updates an existing activity. | -| [Delete Activity](#delete-activity) | Deletes an existing activity. | -| [Delete Conversation Member](#delete-conversation-member) | Removes a member from a conversation. | +| [Delete activity](#delete-activity) | Deletes an existing activity. | +| [Delete conversation member](#delete-conversation-member) | Removes a member from a conversation. | +| [Get activity members](#get-activity-members) | Gets the members of the specified activity within the specified conversation. | +| [Get conversation member](#get-conversation-member) | Gets details about a member of a conversation. | +| [Get conversation members](#get-conversation-members) | Gets the members of the specified conversation. | +| [Get conversation paged members](#get-conversation-paged-members) | Gets the members of the specified conversation one page at a time. | +| [Get conversations](#get-conversations) | Gets a list of conversations the bot has participated in. | +| [Reply to activity](#reply-to-activity) | Sends an activity (message) to the specified conversation, as a reply to the specified activity. | | [Send Conversation History](#send-conversation-history) | Uploads a transcript of past activities to the conversation. | -| [Upload Attachment to Channel](#upload-attachment-to-channel) | Uploads an attachment directly into a channel's blob storage. | +| [Send to conversation](#send-to-conversation) | Sends an activity (message) to the end of the specified conversation. | +| [Update activity](#update-activity) | Updates an existing activity. | +| [Upload attachment to channel](#upload-attachment-to-channel) | Uploads an attachment directly into a channel's blob storage. | -### Create Conversation +### Create conversation Creates a new conversation. @@ -140,38 +155,51 @@ Creates a new conversation. POST /v3/conversations ``` -| | | +| Content | Description | |----|----| | **Request body** | A [ConversationParameters](#conversationparameters-object) object | | **Returns** | A [ConversationResourceResponse](#conversationresourceresponse-object) object | -### Send to Conversation +### Delete activity -Sends an activity (message) to the specified conversation. The activity will be appended to the end of the conversation according to the timestamp or semantics of the channel. To reply to a specific message within the conversation, use [Reply to Activity](#reply-to-activity) instead. +Some channels allow you to delete an existing activity. If successful, this operation removes the specified activity from the specified conversation. ```http -POST /v3/conversations/{conversationId}/activities +DELETE /v3/conversations/{conversationId}/activities/{activityId} ``` -| | | +| Content | Description | |----|----| -| **Request body** | An [Activity](#activity-object) object | -| **Returns** | A [ResourceResponse](#resourceresponse-object) object | +| **Request body** | n/a | +| **Returns** | An HTTP Status code that indicates the outcome of the operation. Nothing is specified in the body of the response. | -### Reply to Activity +### Delete conversation member -Sends an activity (message) to the specified conversation, as a reply to the specified activity. The activity will be added as a reply to another activity, if the channel supports it. If the channel does not support nested replies, then this operation behaves like [Send to Conversation](#send-to-conversation). +Removes a member from a conversation. If that member was the last member of the conversation, the conversation will also be deleted. ```http -POST /v3/conversations/{conversationId}/activities/{activityId} +DELETE /v3/conversations/{conversationId}/members/{memberId} ``` -| | | +| Content | Description | |----|----| -| **Request body** | An [Activity](#activity-object) object | -| **Returns** | A [ResourceResponse](#resourceresponse-object) object | +| **Request body** | n/a | +| **Returns** | An HTTP Status code that indicates the outcome of the operation. Nothing is specified in the body of the response. | + +### Get activity members + +Gets the members of the specified activity within the specified conversation. + +```http +GET /v3/conversations/{conversationId}/activities/{activityId}/members +``` -### Get Conversations +| Content | Description | +|----|----| +| **Request body** | n/a | +| **Returns** | An array of [ChannelAccount](#channelaccount-object) objects | + +### Get conversations Gets a list of conversations the bot has participated in. @@ -179,103 +207,103 @@ Gets a list of conversations the bot has participated in. GET /v3/conversations?continuationToken={continuationToken} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | | **Returns** | A [ConversationsResult](#conversationsresult-object) object | -### Get Conversation Members +### Get conversation member -Gets the members of the specified conversation. +Gets details about a specific member of a specific conversation. ```http -GET /v3/conversations/{conversationId}/members +GET /v3/conversations/{conversationId}/members/{memberId} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | An array of [ChannelAccount](#channelaccount-object) objects | +| **Returns** | A [ChannelAccount](#channelaccount-object) object for the member. | -### Get Conversation Paged Members +### Get conversation members -Gets the members of the specified conversation one page at a time. +Gets the members of the specified conversation. ```http -GET /v3/conversations/{conversationId}/pagedmembers?pageSize={pageSize}&continuationToken={continuationToken} +GET /v3/conversations/{conversationId}/members ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | A [PagedMembersResult](#pagedmembersresult-object) object | +| **Returns** | An array of [ChannelAccount](#channelaccount-object) objects for the members of the conversation. | -### Get Activity Members +### Get conversation paged members -Gets the members of the specified activity within the specified conversation. +Gets the members of the specified conversation one page at a time. ```http -GET /v3/conversations/{conversationId}/activities/{activityId}/members +GET /v3/conversations/{conversationId}/pagedmembers?pageSize={pageSize}&continuationToken={continuationToken} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | An array of [ChannelAccount](#channelaccount-object) objects | +| **Returns** | A [PagedMembersResult](#pagedmembersresult-object) object | -### Update Activity +### Reply to activity -Some channels allow you to edit an existing activity to reflect the new state of a bot conversation. For example, you might remove buttons from a message in the conversation after the user has clicked one of the buttons. If successful, this operation updates the specified activity within the specified conversation. +Sends an activity (message) to the specified conversation, as a reply to the specified activity. The activity will be added as a reply to another activity, if the channel supports it. If the channel doesn't support nested replies, then this operation behaves like [Send to Conversation](#send-to-conversation). ```http -PUT /v3/conversations/{conversationId}/activities/{activityId} +POST /v3/conversations/{conversationId}/activities/{activityId} ``` -| | | +| Content | Description | |----|----| | **Request body** | An [Activity](#activity-object) object | | **Returns** | A [ResourceResponse](#resourceresponse-object) object | -### Delete Activity +### Send conversation history -Some channels allow you to delete an existing activity. If successful, this operation removes the specified activity from the specified conversation. +Uploads a transcript of past activities to the conversation so that the client can render them. ```http -DELETE /v3/conversations/{conversationId}/activities/{activityId} +POST /v3/conversations/{conversationId}/activities/history ``` -| | | +| Content | Description | |----|----| -| **Request body** | n/a | -| **Returns** | An HTTP Status code that indicates the outcome of the operation. Nothing is specified in the body of the response. | +| **Request body** | A [Transcript](#transcript-object) object. | +| **Returns** | A [ResourceResponse](#resourceresponse-object) object. | -### Delete Conversation Member +### Send to conversation -Removes a member from a conversation. If that member was the last member of the conversation, the conversation will also be deleted. +Sends an activity (message) to the specified conversation. The activity will be appended to the end of the conversation according to the timestamp or semantics of the channel. To reply to a specific message within the conversation, use [Reply to Activity](#reply-to-activity) instead. ```http -DELETE /v3/conversations/{conversationId}/members/{memberId} +POST /v3/conversations/{conversationId}/activities ``` -| | | +| Content | Description | |----|----| -| **Request body** | n/a | -| **Returns** | An HTTP Status code that indicates the outcome of the operation. Nothing is specified in the body of the response. | +| **Request body** | An [Activity](#activity-object) object | +| **Returns** | A [ResourceResponse](#resourceresponse-object) object | -### Send Conversation History +### Update activity -Uploads a transcript of past activities to the conversation so that the client can render them. +Some channels allow you to edit an existing activity to reflect the new state of a bot conversation. For example, you might remove buttons from a message in the conversation after the user has clicked one of the buttons. If successful, this operation updates the specified activity within the specified conversation. ```http -POST /v3/conversations/{conversationId}/activities/history +PUT /v3/conversations/{conversationId}/activities/{activityId} ``` -| | | +| Content | Description | |----|----| -| **Request body** | A [Transcript](#transcript-object) object. | -| **Returns** | A [ResourceResponse](#resourceresponse-object) object. | +| **Request body** | An [Activity](#activity-object) object | +| **Returns** | A [ResourceResponse](#resourceresponse-object) object | -### Upload Attachment to Channel +### Upload attachment to channel Uploads an attachment for the specified conversation directly into a channel's blob storage. This enables you to store data in a compliant store. @@ -283,10 +311,10 @@ Uploads an attachment for the specified conversation directly into a channel's b POST /v3/conversations/{conversationId}/attachments ``` -| | | +| Content | Description | |----|----| | **Request body** | An [AttachmentData](#attachmentdata-object) object. | -| **Returns** | A [ResourceResponse](#resourceresponse-object) object. The **id** property specifies the attachment ID that can be used with the [Get Attachment Info](#get-attachment-info) operation and the [Get Attachment](#get-attachment) operation. | +| **Returns** | A [ResourceResponse](#resourceresponse-object) object. The **id** property specifies the attachment ID that can be used with the [Get attachment information](#get-attachment-information) operation and the [Get attachment](#get-attachment) operation. | ## Attachment operations @@ -294,23 +322,23 @@ Use these operations to retrieve information about an attachment as well the bin | Operation | Description | |----|----| -| [Get Attachment Info](#get-attachment-info) | Gets information about the specified attachment, including file name, file type, and the available views (e.g. original or thumbnail). | +| [Get Attachment Info](#get-attachment-information) | Gets information about the specified attachment, including file name, file type, and the available views (for example, original or thumbnail). | | [Get Attachment](#get-attachment) | Gets the specified view of the specified attachment as binary content. | -### Get Attachment Info +### Get attachment information -Gets information about the specified attachment, including file name, type, and the available views (e.g. original or thumbnail). +Gets information about the specified attachment, including file name, type, and the available views (for example, original or thumbnail). ```http GET /v3/attachments/{attachmentId} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | | **Returns** | An [AttachmentInfo](#attachmentinfo-object) object | -### Get Attachment +### Get attachment Gets the specified view of the specified attachment as binary content. @@ -318,14 +346,14 @@ Gets the specified view of the specified attachment as binary content. GET /v3/attachments/{attachmentId}/views/{viewId} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | | **Returns** | Binary content that represents the specified view of the specified attachment | ## State operations (deprecated) -The Microsoft Bot Framework State service is retired as of March 30, 2018. Previously, bots built on the Azure Bot Service or the Bot Builder SDK had a default connection to this service hosted by Microsoft to store bot state data. Bots will need to be updated to use their own state storage. +The Microsoft Bot Framework State service is retired as of March 30, 2018. Previously, bots built on the Azure AI Bot Service or the Bot Builder SDK had a default connection to this service hosted by Microsoft to store bot state data. Bots will need to be updated to use their own state storage. | Operation | Description | |----|----| @@ -345,10 +373,10 @@ The Bot Framework schema defines the objects and their properties that your bot | ---- | ---- | | [Activity object](#activity-object) | Defines a message that is exchanged between bot and user. | | [AnimationCard object](#animationcard-object) | Defines a card that can play animated GIFs or short videos. | -| [Attachment object](#attachment-object) | Defines additional information to include in the message. An attachment may be a media file (e.g. audio, video, image, file) or a rich card. | +| [Attachment object](#attachment-object) | Defines additional information to include in the message. An attachment may be a media file (for example, audio, video, image, file) or a rich card. | | [AttachmentData object](#attachmentdata-object) | Describes an attachment data. | | [AttachmentInfo object](#attachmentinfo-object) | Describes an attachment. | -| [AttachmentView object](#attachmentview-object) | Defines a attachment view. | +| [AttachmentView object](#attachmentview-object) | Defines an object that represents an available view for an attachment. | | [AudioCard object](#audiocard-object) | Defines a card that can play an audio file. | | [CardAction object](#cardaction-object) | Defines an action to perform. | | [CardImage object](#cardimage-object) | Defines an image to display on a card. | @@ -390,48 +418,48 @@ Defines a message that is exchanged between bot and user. | Property | Type | Description | |----|----|----| -| **action** | string | The action to apply or that was applied. Use the **type** property to determine context for the action. For example, if **type** is **contactRelationUpdate**, the value of the **action** property would be **add** if the user added your bot to their contacts list, or **remove** if they removed your bot from their contacts list. | -| **attachmentLayout** | string | Layout of the rich card **attachments** that the message includes. One of these values: **carousel**, **list**. For more information about rich card attachments, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | -| **attachments** | [Attachment](#attachment-object)[] | Array of **Attachment** objects that defines additional information to include in the message. Each attachment may be either a file (e.g. audio, video, image) or a rich card. | -| **callerId** | string | A string containing an IRI identifying the caller of a bot. This field is not intended to be transmitted over the wire, but is instead populated by bots and clients based on cryptographically verifiable data that asserts the identity of the callers (e.g. tokens). | -| **channelData** | object | An object that contains channel-specific content. Some channels provide features that require additional information that cannot be represented using the attachment schema. For those cases, set this property to the channel-specific content as defined in the channel's documentation. For more information, see [Implement channel-specific functionality](bot-framework-rest-connector-channeldata.md). | -| **channelId** | string | An ID that uniquely identifies the channel. Set by the channel. | -| **code** | string | Code indicating why the conversation has ended. | +| **action** | String | The action to apply or that was applied. Use the **type** property to determine context for the action. For example, if **type** is **contactRelationUpdate**, the value of the **action** property would be **add** if the user added your bot to their contacts list, or **remove** if they removed your bot from their contacts list. | +| **attachmentLayout** | String | Layout of the rich card **attachments** that the message includes. One of these values: **carousel**, **list**. For more information about rich card attachments, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | +| **attachments** | [Attachment](#attachment-object)[] | Array of **Attachment** objects that defines additional information to include in the message. Each attachment may be either a file (for example, audio, video, image) or a rich card. | +| **callerId** | String | A string containing an IRI identifying the caller of a bot. This field isn't intended to be transmitted over the wire, but is instead populated by bots and clients based on cryptographically verifiable data that asserts the identity of the callers (for example, tokens). | +| **channelData** | Object | An object that contains channel-specific content. Some channels provide features that require additional information that can't be represented using the attachment schema. For those cases, set this property to the channel-specific content as defined in the channel's documentation. For more information, see [Implement channel-specific functionality](bot-framework-rest-connector-channeldata.md). | +| **channelId** | String | An ID that uniquely identifies the channel. Set by the channel. | +| **code** | String | Code indicating why the conversation has ended. | | **conversation** | [ConversationAccount](#conversationaccount-object) | A **ConversationAccount** object that defines the conversation to which the activity belongs. | -| **deliveryMode** | string | A delivery hint to signal to the recipient alternate delivery paths for the activity. One of these values: **normal**, **notification**. | +| **deliveryMode** | String | A delivery hint to signal to the recipient alternate delivery paths for the activity. One of these values: **normal**, **notification**. | | **entities** | object[] | Array of objects that represents the entities that were mentioned in the message. Objects in this array may be any [Schema.org](http://schema.org/) object. For example, the array may include [Mention](#mention-object) objects that identify someone who was mentioned in the conversation and [Place](#place-object) objects that identify a place that was mentioned in the conversation. | -| **expiration** | string | The time at which the activity should be considered to be "expired" and should not be presented to the recipient. | +| **expiration** | String | The time at which the activity should be considered to be "expired" and shouldn't be presented to the recipient. | | **from** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that specifies the sender of the message. | -| **historyDisclosed** | boolean | Flag that indicates whether or not history is disclosed. Default value is **false**. | -| **id** | string | ID that uniquely identifies the activity on the channel. | -| **importance** | string | Defines the importance of an Activity. One of these values: **low**, **normal**, **high**. | -| **inputHint** | string | Value that indicates whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. One of these values: **acceptingInput**, **expectingInput**, **ignoringInput**. | -| **label** | string | A descriptive label for the activity. | -| **listenFor** | string[] | List of phrases and references that speech and language priming systems should listen for. | -| **locale** | string | Locale of the language that should be used to display text within the message, in the format `-`. The channel uses this property to indicate the user's language, so that your bot may specify display strings in that language. Default value is **en-US**. | -| **localTimestamp** | string | Date and time that the message was sent in the local time zone, expressed in [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) format. | -| **localTimezone** | string | Contains the name of the local timezone of the message, expressed in IANA Time Zone database format. For example, America/Los_Angeles. | +| **historyDisclosed** | Boolean | Flag that indicates whether or not history is disclosed. Default value is **false**. | +| **id** | String | ID that uniquely identifies the activity on the channel. | +| **importance** | String | Defines the importance of an Activity. One of these values: **low**, **normal**, **high**. | +| **inputHint** | String | Value that indicates whether your bot is accepting, expecting, or ignoring user input after the message is delivered to the client. One of these values: **acceptingInput**, **expectingInput**, **ignoringInput**. | +| **label** | String | A descriptive label for the activity. | +| **listenFor** | String[] | List of phrases and references that speech and language priming systems should listen for. | +| **locale** | String | Locale of the language that should be used to display text within the message, in the format `-`. The channel uses this property to indicate the user's language, so that your bot may specify display strings in that language. Default value is **en-US**. | +| **localTimestamp** | String | Date and time that the message was sent in the local time zone, expressed in [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) format. | +| **localTimezone** | String | Contains the name of the local timezone of the message, expressed in IANA Time Zone database format. For example, America/Los_Angeles. | | **membersAdded** | [ChannelAccount](#channelaccount-object)[] | Array of **ChannelAccount** objects that represents the list of users that joined the conversation. Present only if activity **type** is "conversationUpdate" and users joined the conversation. | | **membersRemoved** | [ChannelAccount](#channelaccount-object)[] | Array of **ChannelAccount** objects that represents the list of users that left the conversation. Present only if activity **type** is "conversationUpdate" and users left the conversation. | -| **name** | string | Name of the operation to invoke or the name of the event. | +| **name** | String | Name of the operation to invoke or the name of the event. | | **reactionsAdded** | [MessageReaction](#messagereaction-object)[] | The collection of reactions added to the conversation. | | **reactionsRemoved** | [MessageReaction](#messagereaction-object)[] | The collection of reactions removed from the conversation. | | **recipient** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that specifies the recipient of the message. | | **relatesTo** | [ConversationReference](#conversationreference-object) | A **ConversationReference** object that defines a particular point in a conversation. | -| **replyToId** | string | The ID of the message to which this message replies. To reply to a message that the user sent, set this property to the ID of the user's message. Not all channels support threaded replies. In these cases, the channel will ignore this property and use time ordered semantics (timestamp) to append the message to the conversation. | +| **replyToId** | String | The ID of the message to which this message replies. To reply to a message that the user sent, set this property to the ID of the user's message. Not all channels support threaded replies. In these cases, the channel will ignore this property and use time ordered semantics (timestamp) to append the message to the conversation. | | **semanticAction** |[SemanticAction](#semanticaction-object) | A **SemanticAction** object that represents a reference to a programmatic action. | -| **serviceUrl** | string | URL that specifies the channel's service endpoint. Set by the channel. | -| **speak** | string | Text to be spoken by your bot on a speech-enabled channel. To control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, and pitch, specify this property in [Speech Synthesis Markup Language (SSML)](https://msdn.microsoft.com/library/hh378377(v=office.14).aspx) format. | +| **serviceUrl** | String | URL that specifies the channel's service endpoint. Set by the channel. | +| **speak** | String | Text to be spoken by your bot on a speech-enabled channel. To control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, and pitch, specify this property in [Speech Synthesis Markup Language (SSML)](https://msdn.microsoft.com/library/hh378377(v=office.14).aspx) format. | | **suggestedActions** | [SuggestedActions](#suggestedactions-object) | A **SuggestedActions** object that defines the options from which the user can choose. | -| **summary** | string | Summary of the information that the message contains. For example, for a message that is sent on an email channel, this property may specify the first 50 characters of the email message. | -| **text** | string | Text of the message that is sent from user to bot or bot to user. See the channel's documentation for limits imposed upon the contents of this property. | -| **textFormat** | string | Format of the message's **text**. One of these values: **markdown**, **plain**, **xml**. For details about text format, see [Create messages](bot-framework-rest-connector-create-messages.md). | +| **summary** | String | Summary of the information that the message contains. For example, for a message that is sent on an email channel, this property may specify the first 50 characters of the email message. | +| **text** | String | Text of the message that is sent from user to bot or bot to user. See the channel's documentation for limits imposed upon the contents of this property. | +| **textFormat** | String | Format of the message's **text**. One of these values: **markdown**, **plain**, **xml**. For details about text format, see [Create messages](bot-framework-rest-connector-create-messages.md). | | **textHighlights** | [TextHighlight](#texthighlight-object)[] | The collection of text fragments to highlight when the activity contains a **replyToId** value. | -| **timestamp** | string | Date and time that the message was sent in the UTC time zone, expressed in [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) format. | -| **topicName** | string | Topic of the conversation to which the activity belongs. | -| **type** | string | Type of activity. One of these values: **message**, **contactRelationUpdate**, **conversationUpdate**, **typing**, **endOfConversation**, **event**, **invoke**, **deleteUserData**, **messageUpdate**, **messageDelete**, **installationUpdate**, **messageReaction**, **suggestion**, **trace**, **handoff**. For details about activity types, see [Activities overview](https://aka.ms/botSpecs-activitySchema). | -| **value** | object | Open-ended value. | -| **valueType** | string | The type of the activity's value object. | +| **timestamp** | String | Date and time that the message was sent in the UTC time zone, expressed in [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601) format. | +| **topicName** | String | Topic of the conversation to which the activity belongs. | +| **type** | String | Type of activity. One of these values: **message**, **contactRelationUpdate**, **conversationUpdate**, **typing**, **endOfConversation**, **event**, **invoke**, **deleteUserData**, **messageUpdate**, **messageDelete**, **installationUpdate**, **messageReaction**, **suggestion**, **trace**, **handoff**. For details about activity types, see [Activities overview](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md). | +| **value** | Object | Open-ended value. | +| **valueType** | String | The type of the activity's value object. | [Back to Schema table](#schema) @@ -441,18 +469,18 @@ Defines a card that can play animated GIFs or short videos. | Property | Type | Description | |----|----|----| -| **aspect** | boolean | Aspect ratio of thumbnail/media placeholder. Allowed values are "16:9" and "4:3". | -| **autoloop** | boolean | Flag that indicates whether to replay the list of animated GIFs when the last one ends. Set this property to **true** to automatically replay the animation; otherwise, **false**. The default value is **true**. | -| **autostart** | boolean | Flag that indicates whether to automatically play the animation when the card is displayed. Set this property to **true** to automatically play the animation; otherwise, **false**. The default value is **true**. | +| **aspect** | Boolean | Aspect ratio of thumbnail/media placeholder. Allowed values are "16:9" and "4:3". | +| **autoloop** | Boolean | Flag that indicates whether to replay the list of animated GIFs when the last one ends. Set this property to **true** to automatically replay the animation; otherwise, **false**. The default value is **true**. | +| **autostart** | Boolean | Flag that indicates whether to automatically play the animation when the card is displayed. Set this property to **true** to automatically play the animation; otherwise, **false**. The default value is **true**. | | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to perform one or more actions. The channel determines the number of buttons that you may specify. | -| **duration** | string | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | +| **duration** | String | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | | **image** | [ThumbnailUrl](#thumbnailurl-object) | A **ThumbnailUrl** object that specifies the image to display on the card. | | **media** | [MediaUrl](#mediaurl-object)[] | Array of **MediaUrl** objects. When this field contains more than one URL, each URL is an alternative format of the same content.| -| **shareable** | boolean | Flag that indicates whether the animation may be shared with others. Set this property to **true** if the animation may be shared; otherwise, **false**. The default value is **true**. | -| **subtitle** | string | Subtitle to display under the card's title. | -| **text** | string | Description or prompt to display under the card's title or subtitle. | -| **title** | string | Title of the card. | -| **value** | object | Supplementary parameter for this card. | +| **shareable** | Boolean | Flag that indicates whether the animation may be shared with others. Set this property to **true** if the animation may be shared; otherwise, **false**. The default value is **true**. | +| **subtitle** | String | Subtitle to display under the card's title. | +| **text** | String | Description or prompt to display under the card's title or subtitle. | +| **title** | String | Title of the card. | +| **value** | Object | Supplementary parameter for this card. | [Back to Schema table](#schema) @@ -462,11 +490,11 @@ Defines additional information to include in the message. An attachment may be a | Property | Type | Description | |----|----|----| -| **content** | object | The content of the attachment. If the attachment is a rich card, set this property to the rich card object. This property and the **contentUrl** property are mutually exclusive. | -| **contentType** | string | The media type of the content in the attachment. For media files, set this property to known media types such as **image/png**, **audio/wav**, and **video/mp4**. For rich cards, set this property to one of these vendor-specific types:
  • **application/vnd.microsoft.card.adaptive**: A rich card that can contain any combination of text, speech, images, buttons, and input fields. Set the **content** property to an [AdaptiveCard](https://adaptivecards.io/explorer/AdaptiveCard.html) object.
  • **application/vnd.microsoft.card.animation**: A rich card that plays animation. Set the **content** property to an [AnimationCard](#animationcard-object) object.
  • **application/vnd.microsoft.card.audio**: A rich card that plays audio files. Set the **content** property to an [AudioCard](#audiocard-object) object.
  • **application/vnd.microsoft.card.hero**: A Hero card. Set the **content** property to a [HeroCard](#herocard-object) object.
  • **application/vnd.microsoft.card.receipt**: A Receipt card. Set the **content** property to a [ReceiptCard](#receiptcard-object) object.
  • **application/vnd.microsoft.card.signin**: A user Sign In card. Set the **content** property to a [SignInCard](#signincard-object) object.
  • **application/vnd.microsoft.card.thumbnail**: A Thumbnail card. Set the **content** property to a [ThumbnailCard](#thumbnailcard-object) object.
  • **application/vnd.microsoft.card.video**: A rich card that plays videos. Set the **content** property to a [VideoCard](#videocard-object) object.
| -| **contentUrl** | string | URL for the content of the attachment. For example, if the attachment is an image, you can set **contentUrl** to the URL that represents the location of the image. Supported protocols are: HTTP, HTTPS, File, and Data. | -| **name** | string | Name of the attachment. | -| **thumbnailUrl** | string | URL to a thumbnail image that the channel can use if it supports using an alternative, smaller form of **content** or **contentUrl**. For example, if you set **contentType** to **application/word** and set **contentUrl** to the location of the Word document, you might include a thumbnail image that represents the document. The channel could display the thumbnail image instead of the document. When the user clicks the image, the channel would open the document. | +| **content** | Object | The content of the attachment. If the attachment is a rich card, set this property to the rich card object. This property and the **contentUrl** property are mutually exclusive. | +| **contentType** | String | The media type of the content in the attachment. For media files, set this property to known media types such as **image/png**, **audio/wav**, and **video/mp4**. For rich cards, set this property to one of these vendor-specific types:
  • **application/vnd.microsoft.card.adaptive**: A rich card that can contain any combination of text, speech, images, buttons, and input fields. Set the **content** property to an [AdaptiveCard](https://adaptivecards.io/explorer/AdaptiveCard.html) object.
  • **application/vnd.microsoft.card.animation**: A rich card that plays animation. Set the **content** property to an [AnimationCard](#animationcard-object) object.
  • **application/vnd.microsoft.card.audio**: A rich card that plays audio files. Set the **content** property to an [AudioCard](#audiocard-object) object.
  • **application/vnd.microsoft.card.hero**: A Hero card. Set the **content** property to a [HeroCard](#herocard-object) object.
  • **application/vnd.microsoft.card.receipt**: A Receipt card. Set the **content** property to a [ReceiptCard](#receiptcard-object) object.
  • **application/vnd.microsoft.card.signin**: A user Sign In card. Set the **content** property to a [SignInCard](#signincard-object) object.
  • **application/vnd.microsoft.card.thumbnail**: A Thumbnail card. Set the **content** property to a [ThumbnailCard](#thumbnailcard-object) object.
  • **application/vnd.microsoft.card.video**: A rich card that plays videos. Set the **content** property to a [VideoCard](#videocard-object) object.
| +| **contentUrl** | String | URL for the content of the attachment. For example, if the attachment is an image, you can set **contentUrl** to the URL that represents the location of the image. Supported protocols are: HTTP, HTTPS, File, and Data. | +| **name** | String | Name of the attachment. | +| **thumbnailUrl** | String | URL to a thumbnail image that the channel can use if it supports using an alternative, smaller form of **content** or **contentUrl**. For example, if you set **contentType** to **application/word** and set **contentUrl** to the location of the Word document, you might include a thumbnail image that represents the document. The channel could display the thumbnail image instead of the document. When the user clicks the image, the channel would open the document. | [Back to Schema table](#schema) @@ -476,10 +504,10 @@ Describes an attachment's data. | Property | Type | Description | |----|----|----| -| **name** | string | Name of the attachment. | -| **originalBase64** | string | Attachment content. | -| **thumbnailBase64** | string | Attachment thumbnail content. | -| **type** | string | Content type of the attachment. | +| **name** | String | Name of the attachment. | +| **originalBase64** | String | Attachment content. | +| **thumbnailBase64** | String | Attachment thumbnail content. | +| **type** | String | Content type of the attachment. | [Back to Schema table](#schema) @@ -489,20 +517,20 @@ Metadata for an attachment. | Property | Type | Description | |----|----|----| -| **name** | string | Name of the attachment. | -| **type** | string | Content type of the attachment. | +| **name** | String | Name of the attachment. | +| **type** | String | Content type of the attachment. | | **views** | [AttachmentView](#attachmentview-object)[] | Array of **AttachmentView** objects that represent the available views for the attachment. | [Back to Schema table](#schema) ### AttachmentView object -Defines a attachment view. +Defines an object that represents an available view for an attachment. | Property | Type | Description | |----|----|----| -| **size** | number | Size of the file. | -| **viewId** | string | View ID. | +| **size** | Number | Size of the file. | +| **viewId** | String | View ID. | [Back to Schema table](#schema) @@ -512,18 +540,18 @@ Defines a card that can play an audio file. | Property | Type | Description | |----|----|----| -| **aspect** | string | Aspect ratio of the thumbnail that is specified in the **image** property. Valid values are **16:9** and **4:3**. | -| **autoloop** | boolean | Flag that indicates whether to replay the list of audio files when the last one ends. Set this property to **true** to automatically replay the audio files; otherwise, **false**. The default value is **true**. | -| **autostart** | boolean | Flag that indicates whether to automatically play the audio when the card is displayed. Set this property to **true** to automatically play the audio; otherwise, **false**. The default value is **true**. | +| **aspect** | String | Aspect ratio of the thumbnail that is specified in the **image** property. Valid values are **16:9** and **4:3**. | +| **autoloop** | Boolean | Flag that indicates whether to replay the list of audio files when the last one ends. Set this property to **true** to automatically replay the audio files; otherwise, **false**. The default value is **true**. | +| **autostart** | Boolean | Flag that indicates whether to automatically play the audio when the card is displayed. Set this property to **true** to automatically play the audio; otherwise, **false**. The default value is **true**. | | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to perform one or more actions. The channel determines the number of buttons that you may specify. | -| **duration** | string | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | +| **duration** | String | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | | **image** | [ThumbnailUrl](#thumbnailurl-object) | A **ThumbnailUrl** object that specifies the image to display on the card. | | **media** | [MediaUrl](#mediaurl-object)[] | Array of **MediaUrl** objects. When this field contains more than one URL, each URL is an alternative format of the same content. | -| **shareable** | boolean | Flag that indicates whether the audio files may be shared with others. Set this property to **true** if the audio may be shared; otherwise, **false**. The default value is **true**. | -| **subtitle** | string | Subtitle to display under the card's title. | -| **text** | string | Description or prompt to display under the card's title or subtitle. | -| **title** | string | Title of the card. | -| **value** | object | Supplementary parameter for this card. | +| **shareable** | Boolean | Flag that indicates whether the audio files may be shared with others. Set this property to **true** if the audio may be shared; otherwise, **false**. The default value is **true**. | +| **subtitle** | String | Subtitle to display under the card's title. | +| **text** | String | Description or prompt to display under the card's title or subtitle. | +| **title** | String | Title of the card. | +| **value** | Object | Supplementary parameter for this card. | [Back to Schema table](#schema) @@ -533,13 +561,13 @@ Defines a clickable action with a button. | Property | Type | Description | |----|----|----| -| **channelData** | string | Channel-specific data associated with this action. | -| **displayText** | string | Text to display in the chat feed if the button is clicked. | -| **image** | string | Image URL which will appear on the button, next to the text label. | -| **text** | string | Text for the action. | -| **title** | string | Text description which appears on the button. | -| **type** | string | Type of action to perform. For a list of valid values, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | -| **value** | object | Supplementary parameter for the action. The behavior of this property will vary according to the action **type**. For more information, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | +| **channelData** | String | Channel-specific data associated with this action. | +| **displayText** | String | Text to display in the chat feed if the button is clicked. | +| **image** | String | Image URL that will appear on the button, next to the text label. | +| **text** | String | Text for the action. | +| **title** | String | Text description that appears on the button. | +| **type** | String | Type of action to perform. For a list of valid values, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | +| **value** | Object | Supplementary parameter for the action. The behavior of this property will vary according to the action **type**. For more information, see [Add rich card attachments to messages](bot-framework-rest-connector-add-rich-cards.md). | [Back to Schema table](#schema) @@ -549,9 +577,9 @@ Defines an image to display on a card. | Property | Type | Description | |----|----|----| -| **alt** | string | Description of the image. You should include the description to support accessibility. | +| **alt** | String | Description of the image. You should include the description to support accessibility. | | **tap** | [CardAction](#cardaction-object) | A **CardAction** object that specifies the action to perform if the user taps or clicks the image. | -| **url** | string | URL to the source of the image or the base64 binary of the image (for example, `data:image/png;base64,iVBORw0KGgo...`). | +| **url** | String | URL to the source of the image or the base64 binary of the image (for example, `data:image/png;base64,iVBORw0KGgo...`). | [Back to Schema table](#schema) @@ -561,10 +589,10 @@ Defines a bot or user account on the channel. | Property | Type | Description | |----|----|----| -| **aadObjectId** | string | This account's object ID within Azure Active Directory. | -| **id** | string | Unique ID for the user or bot on this channel. | -| **name** | string | Display-friendly name of the bot or user. | -| **role** | string | Role of the entity behind the account. Either **user** or **bot**. | +| **aadObjectId** | String | This account's object ID within Microsoft Entra ID. | +| **id** | String | Unique ID for the user or bot on this channel. | +| **name** | String | Display-friendly name of the bot or user. | +| **role** | String | Role of the entity behind the account. Either **user** or **bot**. | [Back to Schema table](#schema) @@ -574,13 +602,13 @@ Defines a conversation in a channel. | Property | Type | Description | |----|----|----| -| **aadObjectId** | string | This account's object ID within Azure Active Directory (AAD). | -| **conversationType** | string | Indicates the type of the conversation in channels that distinguish between conversation types (e.g. group, personal). | -| **id** | string | The ID that identifies the conversation. The ID is unique per channel. If the channel starts the conversation, it sets this ID; otherwise, the bot sets this property to the ID that it gets back in the response when it starts the conversation (see [Create Conversation](#create-conversation)). | -| **isGroup** | boolean | Flag to indicate whether the conversation contains more than two participants at the time the activity was generated. Set to **true** if this is a group conversation; otherwise, **false**. The default is **false**. | -| **name** | string | A display name that can be used to identify the conversation. | -| **role** | string | Role of the entity behind the account. Either **user** or **bot**. | -| **tenantId** | string | This conversation's tenant ID. | +| **aadObjectId** | String | This account's object ID within Microsoft Entra ID. | +| **conversationType** | String | Indicates the type of the conversation in channels that distinguish between conversation types (for example, group or personal). | +| **id** | String | The ID that identifies the conversation. The ID is unique per channel. If the channel starts the conversation, it sets this ID; otherwise, the bot sets this property to the ID that it gets back in the response when it starts the conversation (see [Create Conversation](#create-conversation)). | +| **isGroup** | Boolean | Flag to indicate whether the conversation contains more than two participants at the time the activity was generated. Set to **true** if this is a group conversation; otherwise, **false**. The default is **false**. | +| **name** | String | A display name that can be used to identify the conversation. | +| **role** | String | Role of the entity behind the account. Either **user** or **bot**. | +| **tenantId** | String | This conversation's tenant ID. | [Back to Schema table](#schema) @@ -590,7 +618,7 @@ Defines the members of a conversation. | Property | Type | Description | |----|----|----| -| **id** | string | The conversation ID. | +| **id** | String | The conversation ID. | | **members** | [ChannelAccount](#channelaccount-object)[] | List of members in this conversation. | [Back to Schema table](#schema) @@ -601,13 +629,13 @@ Defines parameters for creating a new conversation. | Property | Type | Description | |----|----|----| -| **activity** | [Activity](#activity-object) | The initial message to send to the conversation when it is created. | +| **activity** | [Activity](#activity-object) | The initial message to send to the conversation when it's created. | | **bot** | [ChannelAccount](#channelaccount-object) | Channel account information needed to route a message to the bot. | -| **channelData** | object | Channel-specific payload for creating the conversation. | -| **isGroup** | boolean | Indicates whether this is a group conversation. | +| **channelData** | Object | Channel-specific payload for creating the conversation. | +| **isGroup** | Boolean | Indicates whether this is a group conversation. | | **members** | [ChannelAccount](#channelaccount-object)[] | Channel account information needed to route a message to each user. | -| **tenantId** | string | The tenant ID in which the conversation should be created. | -| **topicName** | string | Topic of the conversation. This property is only used if a channel supports it. | +| **tenantId** | String | The tenant ID in which the conversation should be created. | +| **topicName** | String | Topic of the conversation. This property is only used if a channel supports it. | [Back to Schema table](#schema) @@ -617,11 +645,11 @@ Defines a particular point in a conversation. | Property | Type | Description | |----|----|----| -| **activityId** | string | ID that uniquely identifies the activity that this object references. | +| **activityId** | String | ID that uniquely identifies the activity that this object references. | | **bot** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that identifies the bot in the conversation that this object references. | -| **channelId** | string | An ID that uniquely identifies the channel in the conversation that this object references. | +| **channelId** | String | An ID that uniquely identifies the channel in the conversation that this object references. | | **conversation** | [ConversationAccount](#conversationaccount-object) | A **ConversationAccount** object that defines the conversation that this object references. | -| **serviceUrl** | string | URL that specifies the channel's service endpoint in the conversation that this object references. | +| **serviceUrl** | String | URL that specifies the channel's service endpoint in the conversation that this object references. | | **user** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that identifies the user in the conversation that this object references. | [Back to Schema table](#schema) @@ -632,9 +660,9 @@ Defines a response to [Create Conversation](#create-conversation). | Property | Type | Description | |----|----|----| -| **activityId** | string | ID of the activity, if sent. | -| **id** | string | ID of the resource. | -| **serviceUrl** | string | Service endpoint where operations concerning the conversation may be performed. | +| **activityId** | String | ID of the activity, if sent. | +| **id** | String | ID of the resource. | +| **serviceUrl** | String | Service endpoint where operations concerning the conversation may be performed. | [Back to Schema table](#schema) @@ -645,7 +673,7 @@ Defines the result of [Get Conversations](#get-conversations). | Property | Type | Description | |----|----|----| | **conversations** | [ConversationMembers](#conversationmembers-object)[] | The members in each of the conversations. | -| **continuationToken** | string | The continuation token that can be used in subsequent calls to [Get Conversations](#get-conversations). | +| **continuationToken** | String | The continuation token that can be used in subsequent calls to [Get Conversations](#get-conversations). | [Back to Schema table](#schema) @@ -655,7 +683,7 @@ Metadata object pertaining to an activity. | Property | Type | Description | |----|----|----| -| **type** | string | Type of this entity (RFC 3987 IRI). | +| **type** | String | Type of this entity (RFC 3987 IRI). | [Back to Schema table](#schema) @@ -665,9 +693,9 @@ Object representing error information. | Property | Type | Description | |----|----|----| -| **code** | string | Error code. | +| **code** | String | Error code. | | **innerHttpError** | [InnerHttpError](#innerhttperror-object) | Object representing the inner HTTP error. | -| **message** | string | A description of the error. | +| **message** | String | A description of the error. | [Back to Schema table](#schema) @@ -687,8 +715,8 @@ Defines a key-value pair that contains a fact. | Property | Type | Description | |----|----|----| -| **key** | string | Name of the fact. For example, **Check-in**. The key is used as a label when displaying the fact's value. | -| **value** | string | Value of the fact. For example, **10 October 2016**. | +| **key** | String | Name of the fact. For example, **Check-in**. The key is used as a label when displaying the fact's value. | +| **value** | String | Value of the fact. For example, **10 October 2016**. | [Back to Schema table](#schema) @@ -698,11 +726,11 @@ Defines a geographical location using World Geodetic System (WSG84) coordinates. | Property | Type | Description | |----|----|----| -| **elevation** | number | Elevation of the location. | -| **latitude** | number | Latitude of the location. | -| **longitude** | number | Longitude of the location. | -| **name** | string | Name of the location. | -| **type** | string | The type of this object. Always set to **GeoCoordinates**. | +| **elevation** | Number | Elevation of the location. | +| **latitude** | Number | Latitude of the location. | +| **longitude** | Number | Longitude of the location. | +| **name** | String | Name of the location. | +| **type** | String | The type of this object. Always set to **GeoCoordinates**. | [Back to Schema table](#schema) @@ -714,10 +742,10 @@ Defines a card with a large image, title, text, and action buttons. |----|----|----| | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to perform one or more actions. The channel determines the number of buttons that you may specify. | | **images** | [CardImage](#cardimage-object)[] | Array of **CardImage** objects that specifies the image to display on the card. A Hero card contains only one image. | -| **subtitle** | string | Subtitle to display under the card's title. | +| **subtitle** | String | Subtitle to display under the card's title. | | **tap** | [CardAction](#cardaction-object) | A **CardAction** object that specifies the action to perform if the user taps or clicks the card. This can be the same action as one of the buttons or a different action. | -| **text** | string | Description or prompt to display under the card's title or subtitle. | -| **title** | string | Title of the card. | +| **text** | String | Description or prompt to display under the card's title or subtitle. | +| **title** | String | Title of the card. | [Back to Schema table](#schema) @@ -727,8 +755,8 @@ Object representing an inner HTTP error. | Property | Type | Description | |----|----|----| -| **statusCode** | number | HTTP status code from the failed request. | -| **body** | object | Body from the failed request. | +| **statusCode** | Number | HTTP status code from the failed request. | +| **body** | Object | Body from the failed request. | [Back to Schema table](#schema) @@ -738,7 +766,7 @@ Supplementary parameter for media events. | Property | Type | Description | |----|----|----| -| **cardValue** | object | Callback parameter specified in the **value** field of the media card that originated this event. | +| **cardValue** | Object | Callback parameter specified in the **value** field of the media card that originated this event. | [Back to Schema table](#schema) @@ -748,8 +776,8 @@ Defines the URL to a media file's source. | Property | Type | Description | |----|----|----| -| **profile** | string | Hint that describes the media's content. | -| **url** | string | URL to the source of the media file. | +| **profile** | String | Hint that describes the media's content. | +| **url** | String | URL to the source of the media file. | [Back to Schema table](#schema) @@ -759,9 +787,9 @@ Defines a user or bot that was mentioned in the conversation. | Property | Type | Description | |----|----|----| -| **mentioned** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that specifies the user or the bot that was mentioned. Note that some channels such as Slack assign names per conversation, so it is possible that your bot's mentioned name (in the message's **recipient** property) may be different from the handle that you specified when you [registered](../bot-service-quickstart-registration.md) your bot. However, the account IDs for both would be the same. | -| **text** | string | The user or bot as mentioned in the conversation. For example, if the message is "@ColorBot pick me a new color," this property would be set to **@ColorBot**. Not all channels set this property. | -| **type** | string | This object's type. Always set to **Mention**. | +| **mentioned** | [ChannelAccount](#channelaccount-object) | A **ChannelAccount** object that specifies the user or the bot that was mentioned. Some channels, such as Slack, assign names per conversation, so it's possible that your bot's mentioned name (in the message's **recipient** property) may be different from the handle that you specified when you [registered](../bot-service-quickstart-registration.md) your bot. However, the account IDs for both would be the same. | +| **text** | String | The user or bot as mentioned in the conversation. For example, if the message is "@ColorBot pick me a new color," this property would be set to **\@ColorBot**. Not all channels set this property. | +| **type** | String | This object's type. Always set to **Mention**. | [Back to Schema table](#schema) @@ -771,7 +799,7 @@ Defines a reaction to a message. | Property | Type | Description | |----|----|----| -| **type** | string | Type of reaction. Either **like** or **plusOne**. | +| **type** | String | Type of reaction. Either **like** or **plusOne**. | [Back to Schema table](#schema) @@ -781,7 +809,7 @@ Page of members returned by [Get Conversation Paged Members](#get-conversation-p | Property | Type | Description | |----|----|----| -| **continuationToken** | string | The continuation token that can be used in subsequent calls to [Get Conversation Paged Members](#get-conversation-paged-members). | +| **continuationToken** | String | The continuation token that can be used in subsequent calls to [Get Conversation Paged Members](#get-conversation-paged-members). | | **members** | [ChannelAccount](#channelaccount-object)[] | An array of conversation members. | [Back to Schema table](#schema) @@ -792,11 +820,11 @@ Defines a place that was mentioned in the conversation. | Property | Type | Description | |----|----|----| -| **address** | object | Address of a place. This property can be a **string** or a complex object of type **PostalAddress**. | +| **address** | Object | Address of a place. This property can be a **string** or a complex object of type **PostalAddress**. | | **geo** | [GeoCoordinates](#geocoordinates-object) | A **GeoCoordinates** object that specifies the geographical coordinates of the place. | -| **hasMap** | object | Map to the place. This property can be a **string** (URL) or a complex object of type **Map**. | -| **name** | string | Name of the place. | -| **type** | string | This object's type. Always set to **Place**. | +| **hasMap** | Object | Map to the place. This property can be a **string** (URL) or a complex object of type **Map**. | +| **name** | String | Name of the place. | +| **type** | String | This object's type. Always set to **Place**. | [Back to Schema table](#schema) @@ -810,10 +838,10 @@ Defines a card that contains a receipt for a purchase. | **facts** | [Fact](#fact-object)[] | Array of **Fact** objects that specify information about the purchase. For example, the list of facts for a hotel stay receipt might include the check-in date and check-out date. The channel determines the number of facts that you may specify. | | **items** | [ReceiptItem](#receiptitem-object)[] | Array of **ReceiptItem** objects that specify the purchased items | | **tap** | [CardAction](#cardaction-object) | A **CardAction** object that specifies the action to perform if the user taps or clicks the card. This can be the same action as one of the buttons or a different action. | -| **tax** | string | A currency-formatted string that specifies the amount of tax applied to the purchase. | -| **title** | string | Title displayed at the top of the receipt. | -| **total** | string | A currency-formatted string that specifies the total purchase price, including all applicable taxes. | -| **vat** | string | A currency-formatted string that specifies the amount of value-added tax (VAT) applied to the purchase price. | +| **tax** | String | A currency-formatted string that specifies the amount of tax applied to the purchase. | +| **title** | String | Title displayed at the top of the receipt. | +| **total** | String | A currency-formatted string that specifies the total purchase price, including all applicable taxes. | +| **vat** | String | A currency-formatted string that specifies the amount of value-added tax (VAT) applied to the purchase price. | [Back to Schema table](#schema) @@ -824,12 +852,12 @@ Defines a line item within a receipt. | Property | Type | Description | |----|----|----| | **image** | [CardImage](#cardimage-object) | A **CardImage** object that specifies thumbnail image to display next to the line item. | -| **price** | string | A currency-formatted string that specifies the total price of all units purchased. | -| **quantity** | string | A numeric string that specifies the number of units purchased. | -| **subtitle** | string | Subtitle to be displayed under the line item’s title. | +| **price** | String | A currency-formatted string that specifies the total price of all units purchased. | +| **quantity** | String | A numeric string that specifies the number of units purchased. | +| **subtitle** | String | Subtitle to be displayed under the line item's title. | | **tap** | [CardAction](#cardaction-object) | A **CardAction** object that specifies the action to perform if the user taps or clicks the line item. | -| **text** | string | Description of the line item. | -| **title** | string | Title of the line item. | +| **text** | String | Description of the line item. | +| **title** | String | Title of the line item. | [Back to Schema table](#schema) @@ -839,7 +867,7 @@ Defines a response that contains a resource ID. | Property | Type | Description | |----|----|----| -| **id** | string | ID that uniquely identifies the resource. | +| **id** | String | ID that uniquely identifies the resource. | [Back to Schema table](#schema) @@ -849,9 +877,9 @@ Defines a reference to a programmatic action. | Property | Type | Description | |----|----|----| -| **entities** | object | An object where the value of each property is an [Entity](#entity-object) object. | -| **id** | string | ID of this action. | -| **state** | string | State of this action. Allowed values: **start**, **continue**, **done**. | +| **entities** | Object | An object where the value of each property is an [Entity](#entity-object) object. | +| **id** | String | ID of this action. | +| **state** | String | State of this action. Allowed values: **start**, **continue**, **done**. | [Back to Schema table](#schema) @@ -862,7 +890,7 @@ Defines a card that lets a user sign in to a service. | Property | Type | Description | |----|----|----| | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to sign in to a service. The channel determines the number of buttons that you may specify. | -| **text** | string | Description or prompt to include on the sign in card. | +| **text** | String | Description or prompt to include on the sign-in card. | [Back to Schema table](#schema) @@ -873,7 +901,7 @@ Defines the options from which a user can choose. | Property | Type | Description | |----|----|----| | **actions** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that define the suggested actions. | -| **to** | string[] | Array of strings that contains the IDs of the recipients to whom the suggested actions should be displayed. | +| **to** | String[] | Array of strings that contains the IDs of the recipients to whom the suggested actions should be displayed. | [Back to Schema table](#schema) @@ -883,8 +911,8 @@ Refers to a substring of content within another field. | Property | Type | Description | |----|----|----| -| **occurrence** | number | Occurrence of the text field within the referenced text, if multiple exist. | -| **text** | string | Defines the snippet of text to highlight. | +| **occurrence** | Number | Occurrence of the text field within the referenced text, if multiple exist. | +| **text** | String | Defines the snippet of text to highlight. | [Back to Schema table](#schema) @@ -896,10 +924,10 @@ Defines a card with a thumbnail image, title, text, and action buttons. |----|----|----| | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to perform one or more actions. The channel determines the number of buttons that you may specify. | | **images** | [CardImage](#cardimage-object)[] | Array of **CardImage** objects that specify thumbnail images to display on the card. The channel determines the number of thumbnail images that you may specify. | -| **subtitle** | string | Subtitle to display under the card's title. | +| **subtitle** | String | Subtitle to display under the card's title. | | **tap** | [CardAction](#cardaction-object) | A **CardAction** object that specifies the action to perform if the user taps or clicks the card. This can be the same action as one of the buttons or a different action. | -| **text** | string | Description or prompt to display under the card's title or subtitle. | -| **title** | string | Title of the card. | +| **text** | String | Description or prompt to display under the card's title or subtitle. | +| **title** | String | Title of the card. | [Back to Schema table](#schema) @@ -909,8 +937,8 @@ Defines the URL to an image's source. | Property | Type | Description | |----|----|----| -| **alt** | string | Description of the image. You should include the description to support accessibility. | -| **url** | string | URL to the source of the image or the base64 binary of the image (for example, `data:image/png;base64,iVBORw0KGgo...`). | +| **alt** | String | Description of the image. You should include the description to support accessibility. | +| **url** | String | URL to the source of the image or the base64 binary of the image (for example, `data:image/png;base64,iVBORw0KGgo...`). | [Back to Schema table](#schema) @@ -930,17 +958,17 @@ Defines a card that can play videos. | Property | Type | Description | |----|----|----| -| **aspect** | string | Aspect ratio of the video. Either **16:9** or **4:3**. | -| **autoloop** | boolean | Flag that indicates whether to replay the list of videos when the last one ends. Set this property to **true** to automatically replay the videos; otherwise, **false**. The default value is **true**. | -| **autostart** | boolean | Flag that indicates whether to automatically play the videos when the card is displayed. Set this property to **true** to automatically play the videos; otherwise, **false**. The default value is **true**. | +| **aspect** | String | Aspect ratio of the video. Either **16:9** or **4:3**. | +| **autoloop** | Boolean | Flag that indicates whether to replay the list of videos when the last one ends. Set this property to **true** to automatically replay the videos; otherwise, **false**. The default value is **true**. | +| **autostart** | Boolean | Flag that indicates whether to automatically play the videos when the card is displayed. Set this property to **true** to automatically play the videos; otherwise, **false**. The default value is **true**. | | **buttons** | [CardAction](#cardaction-object)[] | Array of **CardAction** objects that enable the user to perform one or more actions. The channel determines the number of buttons that you may specify. | -| **duration** | string | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | +| **duration** | String | The length of the media content, in [ISO 8601 duration format](https://www.iso.org/iso-8601-date-and-time-format.html). | | **image** | [ThumbnailUrl](#thumbnailurl-object) | A **ThumbnailUrl** object that specifies the image to display on the card. | | **media** | [MediaUrl](#mediaurl-object)[] | Array of **MediaUrl**. When this field contains more than one URL, each URL is an alternative format of the same content. | -| **shareable** | boolean | Flag that indicates whether the videos may be shared with others. Set this property to **true** if the videos may be shared; otherwise, **false**. The default value is **true**. | -| **subtitle** | string | Subtitle to display under the card's title. | -| **text** | string | Description or prompt to display under the card's title or subtitle. | -| **title** | string | Title of the card. | -| **value** | object | Supplementary parameter for this card | +| **shareable** | Boolean | Flag that indicates whether the videos may be shared with others. Set this property to **true** if the videos may be shared; otherwise, **false**. The default value is **true**. | +| **subtitle** | String | Subtitle to display under the card's title. | +| **text** | String | Description or prompt to display under the card's title or subtitle. | +| **title** | String | Title of the card. | +| **value** | Object | Supplementary parameter for this card | [Back to Schema table](#schema) diff --git a/articles/rest-api/bot-framework-rest-connector-authentication.md b/articles/rest-api/bot-framework-rest-connector-authentication.md index 38fc0920b..d20ffe766 100644 --- a/articles/rest-api/bot-framework-rest-connector-authentication.md +++ b/articles/rest-api/bot-framework-rest-connector-authentication.md @@ -1,33 +1,29 @@ --- -title: Authenticate requests - Bot Service -description: Learn how to authenticate API requests in the Bot Connector API and Bot State API. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +title: Authenticate requests with the Bot Connector API +description: Learn how to authenticate API requests in the Bot Connector API and Bot State API. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +ms.custom: + - evergreen --- -# Authentication +# Authentication with the Bot Connector API -Your bot communicates with the Bot Connector service using HTTP over a secured channel (SSL/TLS). -When your bot sends a request to the Connector service, it must include information that the Connector service can use to verify its identity. -Likewise, when the Connector service sends a request to your bot, it must include information that the bot can use to verify its identity. -This article describes the authentication technologies and requirements for the service-level authentication that takes place between a bot and the Bot Connector service. If you are writing your own authentication code, you must implement the security procedures described in this article to enable your bot to exchange messages with the Bot Connector service. +Your bot communicates with the Bot Connector service using HTTP over a secured channel (SSL/TLS). +When your bot sends a request to the Connector service, it must include information that the Connector service can use to verify its identity. +Likewise, when the Connector service sends a request to your bot, it must include information that the bot can use to verify its identity. +This article describes the authentication technologies and requirements for the service-level authentication that takes place between a bot and the Bot Connector service. If you're writing your own authentication code, you must implement the security procedures described in this article to enable your bot to exchange messages with the Bot Connector service. > [!IMPORTANT] -> If you are writing your own authentication code, it is critical that you implement all security procedures correctly. -> By implementing all steps in this article, you can mitigate the risk of an attacker being able to read messages that -> are sent to your bot, send messages that impersonate your bot, and steal secret keys. +> If you're writing your own authentication code, it's critical that you implement all security procedures correctly. +> By implementing all steps in this article, you can mitigate the risk of an attacker being able to read messages that +> are sent to your bot, send messages that impersonate your bot, and steal secret keys. -If you are using the [Bot Framework SDK for .NET](../dotnet/bot-builder-dotnet-overview.md) or the [Bot Framework SDK for Node.js](../nodejs/index.md), you do not need to implement the security procedures described in this article, because the SDK automatically does it for you. Simply configure your project with the App ID and password that you obtained for your bot during [registration](../bot-service-quickstart-registration.md) and the SDK will handle the rest. - -> [!WARNING] -> In December 2016, v3.1 of the Bot Framework security protocol introduced changes to several values that are -> used during token generation and validation. In late fall of 2017, v3.2 of the Bot Framework security protocol was introduced -> which included changes to values that are used during token generation and validation. -> For more information, see [Security protocol changes](#security-protocol-changes). +If you're using the [Bot Framework SDK](../index-bf-sdk.yml), you don't need to implement the security procedures described in this article, because the SDK automatically does it for you. Simply configure your project with the App ID and password that you obtained for your bot during [registration](../bot-service-quickstart-registration.md) and the SDK will handle the rest. ## Authentication technologies @@ -36,30 +32,42 @@ Four authentication technologies are used to establish trust between a bot and t | Technology | Description | |----|----| | **SSL/TLS** | SSL/TLS is used for all service-to-service connections. `X.509v3` certificates are used to establish the identity of all HTTPS services. **Clients should always inspect service certificates to ensure they are trusted and valid.** (Client certificates are NOT used as part of this scheme.) | -| **OAuth 2.0** | OAuth 2.0 uses the Azure Active Directory (Azure AD) v2 account login service to generate a secure token that a bot can use to send messages. This token is a service-to-service token; no user login is required. | +| **OAuth 2.0** | OAuth 2.0 uses the Microsoft Entra ID account login service to generate a secure token that a bot can use to send messages. This token is a service-to-service token; no user login is required. | | **JSON Web Token (JWT)** | JSON Web Tokens are used to encode tokens that are sent to and from the bot. **Clients should fully verify all JWT tokens that they receive**, according to the requirements outlined in this article. | | **OpenID metadata** | The Bot Connector service publishes a list of valid tokens that it uses to sign its own JWT tokens to OpenID metadata at a well-known, static endpoint. | -This article describes how to use these technologies via standard HTTPS and JSON. No special SDKs are required, although you may find that helpers for OpenID etc. are useful. +This article describes how to use these technologies via standard HTTPS and JSON. No special SDKs are required, although you may find that helpers for OpenID and others are useful. ## Authenticate requests from your bot to the Bot Connector service -To communicate with the Bot Connector service, you must specify an access token in the `Authorization` header of each API request, using this format: +To communicate with the Bot Connector service, you must specify an access token in the `Authorization` header of each API request, using this format: ```http Authorization: Bearer ACCESS_TOKEN ``` -This diagram shows the steps for bot-to-connector authentication: +To get and use a JWT token for your bot: -![Authenticate to the MSA login service and then to the bot](../media/connector/auth_bot_to_bot_connector.png) +1. Your bot sends a GET HTTP request to the MSA Login Service. +1. The response from the service contains the JWT token to use. +1. Your bot includes this JWT token in the authorization header in requests to the Bot Connector service. -### Step 1: Request an access token from the Azure AD v2 account login service +### Step 1: Request an access token from the Microsoft Entra ID account login service > [!IMPORTANT] -> If you have not already done so, you must [register your bot](../bot-service-quickstart-registration.md) with the Bot Framework to obtain its AppID and password. You need the bot's App ID and password to request an access token. +> If you haven't already done so, you must [register your bot](../bot-service-quickstart-registration.md) with the Bot Framework to obtain its AppID and password. You need the bot's App ID and password to request an access token. + +Your bot identity can be managed in Azure in a few different ways. + +- As a _user-assigned managed identity_, so that you don't need to manage the bot's credentials yourself. +- As a _single-tenant_ app. +- As a _multi-tenant_ app. + +Request an access token based on your bot's application type. -To request an access token from the login service, issue the following request, replacing **MICROSOFT-APP-ID** and **MICROSOFT-APP-PASSWORD** with the bot's AppID and password that you obtained when you [registered](../bot-service-quickstart-registration.md) your bot with the Bot Framework. +#### [Multi-tenant](#tab/multitenant) + +To request an access token from the login service, issue the following request, replacing **MICROSOFT-APP-ID** and **MICROSOFT-APP-PASSWORD** with the bot's AppID and password that you obtained when you [registered](../bot-service-quickstart-registration.md) your bot with the Bot Service. ```http POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token @@ -69,13 +77,34 @@ Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT-APP-PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default ``` -### Step 2: Obtain the JWT token from the the Azure AD v2 account login service response +#### [Single-tenant](#tab/singletenant) + +To request an access token from the login service, issue the following request, replacing **MICROSOFT-APP-ID**, **MICROSOFT-APP-PASSWORD** and **MICROSOFT-TENANT-ID** with the bot's AppID, password and tenant Id that you obtained when you [registered](../bot-service-quickstart-registration.md) your bot with the Bot Service. + +```http +POST https://login.microsoftonline.com/MICROSOFT-TENANT-ID/oauth2/v2.0/token +Host: login.microsoftonline.com +Content-Type: application/x-www-form-urlencoded + +grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT-APP-PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default +``` + +#### [User-assigned managed identity](#tab/userassigned) + +App Service and Azure Functions provide an internally accessible REST endpoint for token retrieval. To request an access token from the `MSI/token` endpoint, make a GET request to the endpoint. Use the `resource` and `client_id` query parameters. Set `resource` to `https://api.botframework.com` and `client_id` to the bot's managed identity app ID. The request doesn't require other query parameters. + +- For more information about the token endpoint, see [Connect to Azure services in app code](/azure/app-service/overview-managed-identity?context=%2Fazure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fcontext%2Fmsi-context&tabs=portal%2Chttp#connect-to-azure-services-in-app-code). +- For more information about bot app IDs, see [Create an Azure Bot resource](../bot-service-quickstart-registration.md). + +--- + +### Step 2: Obtain the JWT token from the Microsoft Entra ID account login service response If your application is authorized by the login service, the JSON response body will specify your access token, its type, and its expiration (in seconds). -When adding the token to the `Authorization` header of a request, you must use the exact value that is specified in this response (i.e., do not escape or encode the token value). The access token is valid until its expiration. To prevent token expiration from impacting your bot's performance, you may choose to cache and proactively refresh the token. +When adding the token to the `Authorization` header of a request, you must use the exact value that is specified in this response—don't escape or encode the token value. The access token is valid until its expiration. To prevent token expiration from impacting your bot's performance, you may choose to cache and proactively refresh the token. -This example shows a response from the the Azure AD v2 account login service: +This example shows a response from the Microsoft Entra ID account login service: ```http HTTP/1.1 200 OK @@ -100,21 +129,21 @@ Authorization: Bearer ACCESS_TOKEN ``` All requests that you send to the Bot Connector service must include the access token in the `Authorization` header. -If the token is correctly formed, is not expired, and was generated by the the Azure AD v2 account login service, the Bot Connector service will authorize the request. Additional checks are performed to ensure that the token belongs to the bot that sent the request. +If the token is correctly formed, isn't expired, and was generated by the Microsoft Entra ID account login service, the Bot Connector service will authorize the request. Additional checks are performed to ensure that the token belongs to the bot that sent the request. -The following example shows how to specify the access token in the `Authorization` header of the request. +The following example shows how to specify the access token in the `Authorization` header of the request. ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/12345/activities +POST https://smba.trafficmanager.net/teams/v3/conversations/12345/activities Authorization: Bearer eyJhbGciOiJIUzI1Ni... - + (JSON-serialized Activity message goes here) ``` > [!IMPORTANT] > Only specify the JWT token in the `Authorization` header of requests you send to the Bot Connector service. > Do NOT send the token over unsecured channels and do NOT include it in HTTP requests that you send to other services. -> The JWT token that you obtain from the the Azure AD v2 account login service is like a password and should be handled with +> The JWT token that you obtain from the the Microsoft Entra ID account login service is like a password and should be handled with > great care. Anyone that possesses the token may use it to perform operations on behalf of your bot. #### Bot to Connector: example JWT components @@ -143,11 +172,14 @@ payload: ## Authenticate requests from the Bot Connector service to your bot -When the Bot Connector service sends a request to your bot, it specifies a signed JWT token in the `Authorization` header of the request. Your bot can authenticate calls from the Bot Connector service by verifying the authenticity of the signed JWT token. +When the Bot Connector service sends a request to your bot, it specifies a signed JWT token in the `Authorization` header of the request. Your bot can authenticate calls from the Bot Connector service by verifying the authenticity of the signed JWT token. -This diagram shows the steps for connector-to-bot authentication: +To authenticate calls from the Bot Connector service: -![Authenticate calls from the Bot Connector to your bot](../media/connector/auth_bot_connector_to_bot.png) +1. Your bot gets the JWT token from the authorization header in requests sent from the Bot Connector service. +1. Your bot gets the OpenID metadata document for the Bot Connector service. +1. Your bot gets the list of valid signing keys from the document. +1. Your bot verifies the authenticity of the JWT token. ### Step 2: Get the OpenID metadata document @@ -158,7 +190,7 @@ GET https://login.botframework.com/v1/.well-known/openidconfiguration ``` > [!TIP] -> This is a static URL that you can hardcode into your application. +> This is a static URL that you can hardcode into your application. The following example shows an OpenID metadata document that is returned in response to the `GET` request. The `jwks_uri` property specifies the location of the document that contains the Bot Connector service's valid signing keys. @@ -184,36 +216,39 @@ To get the list of valid signing keys, issue a `GET` request via HTTPS to the UR GET https://login.botframework.com/v1/.well-known/keys ``` -The response body specifies the document in the [JWK format](https://tools.ietf.org/html/rfc7517) but also includes an additional property for each key: `endorsements`. The list of keys is relatively stable and may be cached for long periods of time (by default, 5 days within the Bot Framework SDK). +The response body specifies the document in the [JWK format](https://tools.ietf.org/html/rfc7517) but also includes an additional property for each key: `endorsements`. -The `endorsements` property within each key contains one or more endorsement strings which you can use to verify that the channel ID specified in the `channelId` property within the [Activity][] object of the incoming request is authentic. The list of channel IDs that require endorsements is configurable within each bot. By default, it will be the list of all published channel IDs, although bot developers may override selected channel ID values either way. +> [!TIP] +> The list of keys is stable and may be cached, but new keys may be added at any time. To ensure your bot has an up-to-date copy of the document before these keys are used, all bot instances should **refresh their local cache** of the document **at least once every 24 hours**. + +The `endorsements` property within each key contains one or more endorsement strings that you can use to verify that the channel ID specified in the `channelId` property within the [Activity][] object of the incoming request is authentic. The list of channel IDs that require endorsements is configurable within each bot. By default, it will be the list of all published channel IDs, although bot developers may override selected channel ID values either way. ### Step 4: Verify the JWT token -To verify the authenticity of the token that was sent by the Bot Connector service, you must extract the token from the `Authorization` header of the request, parse the token, verify its contents, and verify its signature. +To verify the authenticity of the token that was sent by the Bot Connector service, you must extract the token from the `Authorization` header of the request, parse the token, verify its contents, and verify its signature. -JWT parsing libraries are available for many platforms and most implement secure and reliable parsing for JWT tokens, although you must typically configure these libraries to require that certain characteristics of the token (its issuer, audience, etc.) contain correct values. +JWT parsing libraries are available for many platforms and most implement secure and reliable parsing for JWT tokens, although you must typically configure these libraries to require that certain characteristics of the token (its issuer, audience, and so on) contain correct values. When parsing the token, you must configure the parsing library or write your own validation to ensure the token meets these requirements: 1. The token was sent in the HTTP `Authorization` header with "Bearer" scheme. -2. The token is valid JSON that conforms to the [JWT standard](http://openid.net/specs/draft-jones-json-web-token-07.html). -3. The token contains an "issuer" claim with value of `https://api.botframework.com`. -4. The token contains an "audience" claim with a value equal to the bot's Microsoft App ID. -5. The token is within its validity period. Industry-standard clock-skew is 5 minutes. -6. The token has a valid cryptographic signature, with a key listed in the OpenID keys document that was retrieved in [Step 3](#connector-to-bot-step-3), using the signing algorithm that is specified in the `id_token_signing_alg_values_supported` property of the Open ID Metadata document that was retrieved in [Step 2](#openid-metadata-document). -7. The token contains a "serviceUrl" claim with value that matches the `servieUrl` property at the root of the [Activity][] object of the incoming request. +1. The token is valid JSON that conforms to the [JWT standard](http://openid.net/specs/draft-jones-json-web-token-07.html). +1. The token contains an "issuer" claim with value of `https://api.botframework.com`. +1. The token contains an "audience" claim with a value equal to the bot's Microsoft App ID. +1. The token is within its validity period. Industry-standard clock-skew is 5 minutes. +1. The token has a valid cryptographic signature, with a key listed in the OpenID keys document that was retrieved in [Step 3](#connector-to-bot-step-3), using the signing algorithm that is specified in the `id_token_signing_alg_values_supported` property of the Open ID Metadata document that was retrieved in [Step 2](#openid-metadata-document). +1. The token contains a "serviceUrl" claim with value that matches the `serviceUrl` property at the root of the [Activity][] object of the incoming request. If endorsement for a channel ID is required: -- You should require that any `Activity` object sent to your bot with that channel ID is accompanied by a JWT token that is signed with an endorsement for that channel. -- If the endorsement is not present, your bot should reject the request by returning an **HTTP 403 (Forbidden)** status code. +- You should require that any `Activity` object sent to your bot with that channel ID is accompanied by a JWT token that is signed with an endorsement for that channel. +- If the endorsement isn't present, your bot should reject the request by returning an **HTTP 403 (Forbidden)** status code. > [!IMPORTANT] -> All of these requirements are important, particularly requirements 4 and 6. -> Failure to implement ALL of these verification requirements will leave the bot open to attacks +> All of these requirements are important, particularly requirements 4 and 6. +> Failure to implement ALL of these verification requirements will leave the bot open to attacks > which could cause the bot to divulge its JWT token. -Implementers should not expose a way to disable validation of the JWT token that is sent to the bot. +Implementers shouldn't expose a way to disable validation of the JWT token that is sent to the bot. #### Connector to Bot: example JWT components @@ -240,20 +275,21 @@ payload: ## Authenticate requests from the Bot Framework Emulator to your bot -> [!WARNING] -> In late fall of 2017, v3.2 of the Bot Framework security protocol was introduced. This new version includes a new "issuer" value within tokens that are exchanged between the Bot Framework Eumaltor and your bot. To prepare for this change, the below steps outline how to check for both the v3.1 and v3.2 issuer values. - -The [Bot Framework Emulator](../bot-service-debug-emulator.md) is a desktop tool that you can use to test the functionality of your bot. Although the Bot Framework Emulator uses the same [authentication technologies](#authentication-technologies) as described above, it is unable to impersonate the real Bot Connector service. -Instead, it uses the Microsoft App ID and Microsoft App Password that you specify when you connect the emulator to your bot to create tokens that are identical to those that the bot creates. -When the emulator sends a request to your bot, it specifies the JWT token in the `Authorization` header of the request -- in essence, using the bot's own credentials to authenticate the request. +The [Bot Framework Emulator](../bot-service-debug-emulator.md) is a desktop tool that you can use to test the functionality of your bot. Although the Bot Framework Emulator uses the same [authentication technologies](#authentication-technologies) as described above, it's unable to impersonate the real Bot Connector service. +Instead, it uses the Microsoft App ID and Microsoft App Password that you specify when you connect the Emulator to your bot to create tokens that are identical to those that the bot creates. +When the Emulator sends a request to your bot, it specifies the JWT token in the `Authorization` header of the request—in essence, using the bot's own credentials to authenticate the request. -If you are implementing an authentication library and want to accept requests from the Bot Framework Emulator, you must add this additional verification path. The path is structurally similar to the [Connector -> Bot](#connector-to-bot) verification path, but it uses MSA’s OpenID document instead of the Bot Connector’s OpenID document. +If you're implementing an authentication library and want to accept requests from the Bot Framework Emulator, you must add this additional verification path. The path is structurally similar to the [Connector -> Bot](#connector-to-bot) verification path, but it uses MSA's OpenID document instead of the Bot Connector's OpenID document. -This diagram shows the steps for emulator-to-bot authentication: +To authenticate calls from the Bot Framework Emulator: -![Authenticate calls from the Bot Framework Emulator to your bot](../media/connector/auth_bot_framework_emulator_to_bot.png) +1. Your bot gets the JWT token from the authorization header in requests sent from the Bot Framework Emulator. +1. Your bot gets the OpenID metadata document for the Bot Connector service. +1. Your bot gets the list of valid signing keys from the document. +1. Your bot verifies the authenticity of the JWT token. --- + ### Step 2: Get the MSA OpenID metadata document The OpenID metadata document specifies the location of a second document that lists the valid signing keys. To get the MSA OpenID metadata document, issue this request via HTTPS: @@ -279,35 +315,35 @@ The following example shows an OpenID metadata document that is returned in resp To get the list of valid signing keys, issue a `GET` request via HTTPS to the URL specified by the `jwks_uri` property in the OpenID metadata document. For example: ```http -GET https://login.microsoftonline.com/common/discovery/v2.0/keys +GET https://login.microsoftonline.com/common/discovery/v2.0/keys Host: login.microsoftonline.com ``` -The response body specifies the document in the [JWK format](https://tools.ietf.org/html/rfc7517). +The response body specifies the document in the [JWK format](https://tools.ietf.org/html/rfc7517). ### Step 4: Verify the JWT token -To verify the authenticity of the token that was sent by the emulator, you must extract the token from the `Authorization` header of the request, parse the token, verify its contents, and verify its signature. +To verify the authenticity of the token that was sent by the Emulator, you must extract the token from the `Authorization` header of the request, parse the token, verify its contents, and verify its signature. -JWT parsing libraries are available for many platforms and most implement secure and reliable parsing for JWT tokens, although you must typically configure these libraries to require that certain characteristics of the token (its issuer, audience, etc.) contain correct values. +JWT parsing libraries are available for many platforms and most implement secure and reliable parsing for JWT tokens, although you must typically configure these libraries to require that certain characteristics of the token (its issuer, audience, and so on) contain correct values. When parsing the token, you must configure the parsing library or write your own validation to ensure the token meets these requirements: 1. The token was sent in the HTTP `Authorization` header with "Bearer" scheme. -2. The token is valid JSON that conforms to the [JWT standard](http://openid.net/specs/draft-jones-json-web-token-07.html). -3. The token contains an "issuer" claim with value of `https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/` or `https://sts.windows.net/f8cdef31-a31e-4b4a-93e4-5f571e91255a/`. (Checking for both issuer values will ensure you are checking for both the security protocol v3.1 and v3.2 issuer values) -4. The token contains an "audience" claim with a value equal to the bot's Microsoft App ID. -5. The token contains an "appid" claim with the value equal to the bot's Microsoft App ID. -6. The token is within its validity period. Industry-standard clock-skew is 5 minutes. -7. The token has a valid cryptographic signature with a key listed in the OpenID keys document that was retrieved in [Step 3](#emulator-to-bot-step-3). +1. The token is valid JSON that conforms to the [JWT standard](http://openid.net/specs/draft-jones-json-web-token-07.html). +1. The token contains an "issuer" claim with one of the [highlighted values](https://github.com/microsoft/botbuilder-dotnet/blob/3c335046f95deeac50fbb0b48c7c8c42051d4f6d/libraries/Microsoft.Bot.Connector/Authentication/EmulatorValidation.cs#L28-L31) for non governmental cases. Checking for both issuer values will ensure you're checking for both the security protocol v3.1 and v3.2 issuer values. +1. The token contains an "audience" claim with a value equal to the bot's Microsoft App ID. +1. The Emulator, depending on the version, sends the AppId via either the appid claim (version 1) or the authorized party claim (version 2). +1. The token is within its validity period. Industry-standard clock-skew is 5 minutes. +1. The token has a valid cryptographic signature with a key listed in the OpenID keys document that was retrieved in [Step 3](#emulator-to-bot-step-3). > [!NOTE] -> Requirement 5 is a specific to the emulator verification path. +> Requirement 5 is a specific to the Emulator verification path. -If the token does not meet all of these requirements, your bot should terminate the request by returning an **HTTP 403 (Forbidden)** status code. +If the token doesn't meet all of these requirements, your bot should terminate the request by returning an **HTTP 403 (Forbidden)** status code. > [!IMPORTANT] -> All of these requirements are important, particularly requirements 4 and 7. -> Failure to implement ALL of these verification requirements will leave the bot open to attacks +> All of these requirements are important, particularly requirements 4 and 7. +> Failure to implement ALL of these verification requirements will leave the bot open to attacks > which could cause the bot to divulge its JWT token. #### Emulator to Bot: example JWT components @@ -335,11 +371,6 @@ payload: ## Security protocol changes -> [!WARNING] -> Support for v3.0 of the security protocol was discontinued on **July 31, 2017**. -> If you have written your own authentication code (i.e., did not use the Bot Framework SDK to create your bot), -> you must upgrade to v3.1 of the security protocol by updating your application to use the v3.1 values that are listed below. - ### [Bot to Connector authentication](#bot-to-connector) #### OAuth login URL @@ -380,20 +411,24 @@ payload: | Protocol version | Valid value | |----|----| -| v3.1 & v3.2 | Your bot’s Microsoft App ID + `/.default` | +| v3.1 & v3.2 | Your bot's Microsoft App ID + `/.default` | #### JWT Audience | Protocol version | Valid value | |----|----| -| v3.1 & v3.2 | Your bot’s Microsoft App ID | +| v3.1 & v3.2 | Your bot's Microsoft App ID | #### JWT Issuer | Protocol version | Valid value | |----|----| -| v3.1 | `https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/` | -| v3.2 | `https://sts.windows.net/f8cdef31-a31e-4b4a-93e4-5f571e91255a/` | +| v3.1 1.0| `https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/` | +| v3.1 2.0| `https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0`| +| v3.2 1.0| `https://sts.windows.net/f8cdef31-a31e-4b4a-93e4-5f571e91255a/` | +| v3.2 2.0| `https://login.microsoftonline.com/f8cdef31-a31e-4b4a-93e4-5f571e91255a/v2.0`| + +See also the [highlighted values](https://github.com/microsoft/botbuilder-dotnet/blob/3c335046f95deeac50fbb0b48c7c8c42051d4f6d/libraries/Microsoft.Bot.Connector/Authentication/EmulatorValidation.cs#L28-L31) for non governmental cases. #### OpenID metadata document @@ -404,7 +439,7 @@ payload: ## Additional resources - [Troubleshooting Bot Framework authentication](../bot-service-troubleshoot-authentication-problems.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) - [JSON Web Token (JWT) draft-jones-json-web-token-07](http://openid.net/specs/draft-jones-json-web-token-07.html) - [JSON Web Signature (JWS) draft-jones-json-web-signature-04](https://tools.ietf.org/html/draft-jones-json-web-signature-04) - [JSON Web Key (JWK) RFC 7517](https://tools.ietf.org/html/rfc7517) diff --git a/articles/rest-api/bot-framework-rest-connector-channeldata.md b/articles/rest-api/bot-framework-rest-connector-channeldata.md index 7014db0ce..edb2621f6 100644 --- a/articles/rest-api/bot-framework-rest-connector-channeldata.md +++ b/articles/rest-api/bot-framework-rest-connector-channeldata.md @@ -1,45 +1,47 @@ --- title: Implement channel-specific functionality using REST API - Bot Service description: Learn how to implement channel-specific functionality using the Bot Connector API. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Implement channel-specific functionality +# Implement channel-specific functionality with the Bot Connector API -Some channels provide features that cannot be implemented by using only [message text and attachments](bot-framework-rest-connector-create-messages.md). To implement channel-specific functionality, you can pass native metadata to a channel in the [Activity[]] object's `channelData` property. For example, your bot can use the `channelData` property to instruct Telegram to send a sticker or to instruct Office365 to send an email. +Some channels provide features that can't be implemented by using only [message text and attachments](bot-framework-rest-connector-create-messages.md). To implement channel-specific functionality, you can pass native metadata to a channel in the [Activity][] object's `channelData` property. For example, your bot can use the `channelData` property to instruct Telegram to send a sticker or to instruct Office365 to send an email. + +[Activity]: bot-framework-rest-connector-api-reference.md#activity-object This article describes how to use a message activity's `channelData` property to implement this channel-specific functionality: -| Channel | Functionality | -|----|----| -| Email | Send and receive an email that contains body, subject, and importance metadata | -| Slack | Send full fidelity Slack messages | -| Facebook | Send Facebook notifications natively | -| Telegram | Perform Telegram-specific actions, such as sharing a voice memo or a sticker | -| Kik | Send and receive native Kik messages | +| Channel | Functionality | +| -------- | ------------------------------------------------------------------------------ | +| Email | Send and receive an email that contains body, subject, and importance metadata | +| Slack | Send full fidelity Slack messages | +| Facebook | Send Facebook notifications natively | +| Telegram | Perform Telegram-specific actions, such as sharing a voice memo or a sticker | > [!NOTE] -> The value of an `Activity` object's `channelData` property is a JSON object. -> The structure of the JSON object will vary according to the channel and the functionality being implemented, as described below. +> The value of an `Activity` object's `channelData` property is a JSON object. +> The structure of the JSON object will vary according to the channel and the functionality being implemented, as described below. -## Create a custom Email message +## Create a custom email message To create an email message, set the `Activity` object's `channelData` property to a JSON object that contains these properties: -[!INCLUDE [Email channelData table](~/includes/snippet-channelData-email.md)] +[!INCLUDE [email channelData table](~/includes/snippet-channelData-email.md)] This snippet shows an example of the `channelData` property for a custom email message. ```json "channelData": { - "htmlBody": "This is more than awesome.", - "subject": "Super awesome message subject", + "htmlBody": "This is more than awesome.", "importance": "high", "ccRecipients": "Yasemin@adatum.com;Temel@adventure-works.com" } @@ -47,11 +49,11 @@ This snippet shows an example of the `channelData` property for a custom email m ## Create a full-fidelity Slack message -To create a full-fidelity Slack message, set the `Activity` object's `channelData` property to a JSON object that specifies -Slack messages, Slack attachments, and/or Slack buttons. +To create a full-fidelity Slack message, set the `Activity` object's `channelData` property to a JSON object that specifies +[Slack messages](https://api.slack.com/docs/messages), [Slack attachments](https://api.slack.com/docs/message-attachments), and/or [Slack buttons](https://api.slack.com/docs/message-buttons). > [!NOTE] -> To support buttons in Slack messages, you must enable **Interactive Messages** when you +> To support buttons in Slack messages, you must enable **Interactive Messages** when you > [connect your bot](../bot-service-manage-channels.md) to the Slack channel. This snippet shows an example of the `channelData` property for a custom Slack message. @@ -109,8 +111,8 @@ This snippet shows an example of the `channelData` property for a custom Slack m } ``` -When a user clicks a button within a Slack message, your bot will receive a response message in which the `channelData` property is populated with a `payload` JSON object. The `payload` object specifies contents of the original message, -identifies the button that was clicked, and identifies the user who clicked the button. +When a user clicks a button within a Slack message, your bot will receive a response message in which the `channelData` property is populated with a `payload` JSON object. The `payload` object specifies contents of the original message, +identifies the button that was clicked, and identifies the user who clicked the button. This snippet shows an example of the `channelData` property in the message that a bot receives when a user clicks a button in the Slack message. @@ -123,27 +125,27 @@ This snippet shows an example of the `channelData` property in the message that "value": "yes" } ], - . . . - "original_message": "{…}", + //... + "original_message": "{...}", "response_url": "https://hooks.slack.com/actions/..." } } ``` -Your bot can reply to this message in the [normal manner](bot-framework-rest-connector-send-and-receive-messages.md#create-reply), or it can post its response directly to the endpoint that is specified by the `payload` object's `response_url` property. For information about when and how to post a response to the `response_url`, see Slack Buttons. +Your bot can reply to this message in the [normal manner](bot-framework-rest-connector-send-and-receive-messages.md#create-reply), or it can post its response directly to the endpoint that is specified by the `payload` object's `response_url` property. For information about when and how to post a response to the `response_url`, see [Slack Buttons](https://api.slack.com/docs/message-buttons). ## Create a Facebook notification -To create a Facebook notification, set the `Activity` object's `channelData` property to a JSON object that specifies these properties: +To create a Facebook notification, set the `Activity` object's `channelData` property to a JSON object that specifies these properties: | Property | Description | |----|----| -| notification_type | The type of notification (e.g., **REGULAR**, **SILENT_PUSH**, **NO_PUSH**). +| notification_type | The type of notification (such as, **REGULAR**, **SILENT_PUSH**, or **NO_PUSH**). | attachment | An attachment that specifies an image, video, or other multimedia type, or a templated attachment such as a receipt. | > [!NOTE] -> For details about format and contents of the `notification_type` property and `attachment` property, see the -> Facebook API documentation. +> For details about format and contents of the `notification_type` property and `attachment` property, see the +> [Facebook API documentation](https://developers.facebook.com/docs/messenger-platform/send-api-reference#guidelines). This snippet shows an example of the `channelData` property for a Facebook receipt attachment. @@ -151,10 +153,10 @@ This snippet shows an example of the `channelData` property for a Facebook recei "channelData": { "notification_type": "NO_PUSH", "attachment": { - "type": "template" + "type": "template", "payload": { "template_type": "receipt", - . . . + //... } } } @@ -162,23 +164,23 @@ This snippet shows an example of the `channelData` property for a Facebook recei ## Create a Telegram message -To create a message that implements Telegram-specific actions, -such as sharing a voice memo or a sticker, -set the `Activity` object's `channelData` property to a JSON object that specifies these properties: +To create a message that implements Telegram-specific actions, +such as sharing a voice memo or a sticker, +set the `Activity` object's `channelData` property to a JSON object that specifies these properties: | Property | Description | |----|----| | method | The Telegram Bot API method to call. | | parameters | The parameters of the specified method. | -These Telegram methods are supported: +These Telegram methods are supported: - answerInlineQuery - editMessageCaption - editMessageReplyMarkup - editMessageText - forwardMessage -- kickChatMember +- banChatMember - sendAudio - sendChatAction - sendContact @@ -190,15 +192,15 @@ These Telegram methods are supported: - sendVenue - sendVideo - sendVoice -- unbanChateMember +- unbanChatMember -For details about these Telegram methods and their parameters, see the -Telegram Bot API documentation. +For details about these Telegram methods and their parameters, see the [Telegram Bot API documentation](https://core.telegram.org/bots/api#available-methods). > [!NOTE] ->
  • The chat_id parameter is common to all Telegram methods. If you do not specify chat_id as a parameter, the framework will provide the ID for you.
  • ->
  • Instead of passing file contents inline, specify the file using a URL and media type as shown in the example below.
  • ->
  • Within each message that your bot receives from the Telegram channel, the channelData property will include the message that your bot sent previously.
+> +> - The `chat_id` parameter is common to all Telegram methods. If you don't specify `chat_id` as a parameter, the framework will provide the ID for you. +> - Instead of passing file contents inline, specify the file using a URL and media type as shown in the example below. +> - Within each message that your bot receives from the Telegram channel, the `channelData` property will include the message that your bot sent previously. This snippet shows an example of a `channelData` property that specifies a single Telegram method. @@ -237,46 +239,9 @@ This snippet shows an example of a `channelData` property that specifies an arra ] ``` -## Create a native Kik message - -To create a native Kik message, -set the `Activity` object's `channelData` property to a JSON object that specifies this property: - -| Property | Description | -|----|----| -| messages | An array of Kik messages. For details about Kik message format, see Kik Message Formats. | - -This snippet shows an example of the `channelData` property for a native Kik message. - -```json -"channelData": { - "messages": [ - { - "chatId": "c6dd8165…", - "type": "link", - "to": "kikhandle", - "title": "My Webpage", - "text": "Some text to display", - "url": "http://botframework.com", - "picUrl": "http://lorempixel.com/400/200/", - "attribution": { - "name": "My App", - "iconUrl": "http://lorempixel.com/50/50/" - }, - "noForward": true, - "kikJsData": { - "key": "value" - } - } - ] -} -``` - ## Additional resources - [Create messages](bot-framework-rest-connector-create-messages.md) - [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) - [Channels reference](../bot-service-channels-reference.md) - -[Activity]: bot-framework-rest-connector-api-reference.md#activity-object diff --git a/articles/rest-api/bot-framework-rest-connector-concepts.md b/articles/rest-api/bot-framework-rest-connector-concepts.md index b982406c4..a6219d540 100644 --- a/articles/rest-api/bot-framework-rest-connector-concepts.md +++ b/articles/rest-api/bot-framework-rest-connector-concepts.md @@ -1,48 +1,63 @@ --- -title: Key concepts in the Bot Connector and Bot State services - Bot Service -description: Understand key concepts in the Bot Framework's Bot Connector service and Bot State service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/16/2019 +title: Key concepts in the Bot Connector API +description: Understand key concepts in the Bot Framework Connector service and Bot State service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: concept-article +ms.custom: + - evergreen --- -# Key concepts +# Key concepts in the Bot Connector API -You can use the Bot Connector service to communicate with users across multiple channels such as Skype, Email, Slack, and more. This article introduces key concepts in the Bot Connector service. +The Bot Framework and the Azure AI Bot Service allow your bot to communicate with users on Teams, Facebook, and more. Channels are available in two forms: -## Bot Connector service +- As a service included as part of Azure AI Bot Service. +- As adapter libraries for use with the Bot Framework SDK. -The Bot Connector service enables your bot to exchange messages with channels configured in the [Azure Portal](https://portal.azure.com). It uses industry-standard REST and JSON over HTTPS and enables authentication with JWT Bearer tokens. For detailed information about how to use the Bot Connector service, see [Authentication](bot-framework-rest-connector-authentication.md) and the remaining articles in this section. +This article focuses on the standard channels included in the Azure AI Bot Service. -### Activity +## Bot Framework Channels -The Bot Connector service exchanges information between between bot and channel (user) by passing an [Activity][Activity] object. The most common type of activity is **message**, but there are other activity types that can be used to communicate various types of information to a bot or channel. For details about Activities in the Bot Connector service, see [Activities overview](https://aka.ms/botSpecs-activitySchema). +Bot Framework channels enable your bot to exchange messages with channels configured in the [Azure portal](https://portal.azure.com). It uses industry-standard REST and JSON over HTTPS and enables authentication with JWT Bearer tokens. For detailed information about how to use the Bot Connector service, see [Authentication](bot-framework-rest-connector-authentication.md) and the remaining articles in this section. -## Bot State service +### Activity -The Microsoft Bot Framework State service is retired as of March 30, 2018. Previously, bots built on the Azure Bot Service or the Bot Builder SDK had a default connection to this service hosted by Microsoft to store bot state data. Bots will need to be updated to use their own state storage. +The Connector service exchanges information between bot and channel (user) by passing an [Activity][Activity] object. The most common type of activity is **message**, but there are other activity types that can be used to communicate various types of information to a bot or channel. For details about Activities in the Bot Connector service, see [Activities overview](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md). ## Authentication -Both the Bot Connector service enables authentication with JWT Bearer tokens. For detailed information about how to authenticate outbound requests that your bot sends to the Bot Framework, how to authenticate inbound requests that your bot receives from the Bot Framework, and more, see [Authentication](bot-framework-rest-connector-authentication.md). +The Bot Framework Service uses JWT Bearer tokens for authentication. For detailed information about how to authenticate outbound requests that your bot sends to the Bot Framework and how to authenticate inbound requests that your bot receives from the Bot Framework, see [Authentication](bot-framework-rest-connector-authentication.md). ## Client libraries -The Bot Framework provides client libraries that can be used to build bots in either C# or Node.js. +The Bot Framework provides client libraries that can be used to build bots in C#, JavaScript, Python, and Java. + +- [Bot Framework SDK for C#](/dotnet/api/). +- [Bot Framework SDK for Node.js](/javascript/api/botbuilder/). +- [Bot Framework SDK for Python](/python/api/). +- [Bot Framework SDK for Java](https://github.com/microsoft/botbuilder-java#readme). + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] -- To build a bot using C#, use the [Bot Framework SDK for C#](../dotnet/bot-builder-dotnet-overview.md). -- To build a bot using Node.js, use the [Bot Framework SDK for Node.js](../nodejs/index.md). +In addition to simplifying calls to Bot Framework REST APIs, each Bot Framework SDK also provides support for building dialogs that encapsulate conversational logic, built-in prompts for simple things such as Yes/No, strings, numbers, and enumerations, built-in support for powerful AI frameworks such as [LUIS](https://www.luis.ai/), and more. -In addition to modeling the Bot Connector service, each Bot Framework SDK also provides a powerful system for building dialogs that encapsulate conversational logic, built-in prompts for simple things such as Yes/No, strings, numbers, and enumerations, built-in support for powerful AI frameworks such as LUIS, and more. +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] > [!NOTE] -> As an alternative to using the C# SDK or Node.js SDK, you can generate your own client library in the language of your choice by using the Bot Connector Swagger file. +> As an alternative to using the these SDKs, you can generate your own client library in the language of your choice by using the [Bot Connector Swagger file](https://github.com/Microsoft/botbuilder-dotnet/blob/master/libraries/Swagger/ConnectorAPI.json) or code direct to its REST API. + +## Bot State service + +The Microsoft Bot Framework State service is retired as of March 30, 2018. Previously, bots built on the Azure AI Bot Service or the Bot Builder SDK had a default connection to this service hosted by Microsoft to store bot state data. Bots will need to be updated to use their own state storage. -## Additional resources +## Additional information -Learn more about building bots using the Bot Connector service by reviewing articles throughout this section, beginning with [Authentication](bot-framework-rest-connector-authentication.md). If you encounter problems or have suggestions regarding the Bot Connector service, see [Support](../bot-service-resources-links-help.md) for a list of available resources. +Learn more about building bots using the Connector service by reviewing articles throughout this section, beginning with [Authentication](bot-framework-rest-connector-authentication.md). If you encounter problems or have suggestions regarding the Connector service, see [Support](../bot-service-resources-links-help.md) for a list of available resources. [Activity]: bot-framework-rest-connector-api-reference.md#activity-object diff --git a/articles/rest-api/bot-framework-rest-connector-create-messages.md b/articles/rest-api/bot-framework-rest-connector-create-messages.md index df1d343b9..5ce8e0265 100644 --- a/articles/rest-api/bot-framework-rest-connector-create-messages.md +++ b/articles/rest-api/bot-framework-rest-connector-create-messages.md @@ -1,52 +1,48 @@ --- -title: Create messages with the Bot Connector API - Bot Service -description: Learn about commonly-used message properties within the Bot Connector API. -author: RobStand -ms.author: kamrani -manager: kamrani +title: Create messages with the Bot Framework Connector service - Azure AI Bot Service +description: Become familiar with the messages that bots use to communicate with users. Learn about properties used to format text, attach files, and specify other behavior. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Create messages +# Create messages with the Bot Connector API -Your bot will send [Activity][] objects of type **message** to communicate information to users, and likewise, will also receive **message** activities from users. Some messages may simply consist of plain text, while others may contain richer content such as [text to be spoken](bot-framework-rest-connector-text-to-speech.md), [suggested actions](bot-framework-rest-connector-add-suggested-actions.md), [media attachments](bot-framework-rest-connector-add-media-attachments.md), [rich cards](bot-framework-rest-connector-add-rich-cards.md), and [channel-specific data](bot-framework-rest-connector-channeldata.md). This article describes some of the commonly-used message properties. +Your bot will send [Activity][] objects of type **message** to communicate information to users, and likewise, will also receive **message** activities from users. Some messages may simply consist of plain text, while others may contain richer content such as [suggested actions](bot-framework-rest-connector-add-suggested-actions.md), [media attachments](bot-framework-rest-connector-add-media-attachments.md), [rich cards](bot-framework-rest-connector-add-rich-cards.md), and [channel-specific data](bot-framework-rest-connector-channeldata.md). This article describes some of the commonly-used message properties. ## Message text and formatting -Message text can be formatted using **plain**, **markdown**, or **xml**. The default format for the `textFormat` property is **markdown** and interprets text using Markdown formatting standards. The level of text format support varies across channels. +Message text can be formatted using **plain**, **markdown**, or **xml**. The default format for the `textFormat` property is **markdown** and interprets text using Markdown formatting standards. The level of text format support varies across channels. [!INCLUDE [Channel Inspector intro](~/includes/snippet-channel-inspector.md)] -The `textFormat` property of the [Activity][] object can be used to specify the format of the text. For example, to create a basic message that contains only plain text, set the `textFormat` property of the `Activity` object to **plain**, set the `text` property to the contents of the message and set the `locale` property to the locale of the sender. +The `textFormat` property of the [Activity][] object can be used to specify the format of the text. For example, to create a basic message that contains only plain text, set the `textFormat` property of the `Activity` object to **plain**, set the `text` property to the contents of the message and set the `locale` property to the locale of the sender. ## Attachments -The `attachments` property of the [Activity][] object can be used to send simple media attachments -(image, audio, video, file) and rich cards. For details, see [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) and [Add rich cards to messages](bot-framework-rest-connector-add-rich-cards.md). +The `attachments` property of the [Activity][] object can be used to send simple media attachments (image, audio, video, file) and rich cards. For details, see [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) and [Add rich cards to messages](bot-framework-rest-connector-add-rich-cards.md). ## Entities -The `entities` property of the [Activity][] object is an array of open-ended schema.org objects that allows the exchange of common contextual metadata between the channel and bot. +The `entities` property of the [Activity][] object is an array of open-ended [schema.org](https://schema.org/) objects that allows the exchange of common contextual metadata between the channel and bot. ### Mention entities -Many channels support the ability for a bot or user to "mention" someone within the context of a conversation. -To mention a user in a message, populate the message's `entities` property with a [Mention][] object. +Many channels support the ability for a bot or user to "mention" someone within the context of a conversation. To mention a user in a message, populate the message's `entities` property with a [Mention][] object. ### Place entities -To convey location-related information within a message, populate the message's `entities` property with [Place][] object. +To convey location-related information within a message, populate the message's `entities` property with [Place][] objects. ## Channel data -The `channelData` property of the [Activity][] object can be used to implement channel-specific functionality. -For details, see [Implement channel-specific functionality](bot-framework-rest-connector-channeldata.md). - -## Text to speech - -The `speak` property of the [Activity][] object can be used to specify the text to be spoken by your bot on a speech-enabled channel and the `inputHint` property of the `Activity` object can be used to influence the state of the client's microphone. For details, see [Add speech to messages](bot-framework-rest-connector-text-to-speech.md) and [Add input hints to messages](bot-framework-rest-connector-add-input-hints.md). +The `channelData` property of the [Activity][] object can be used to implement channel-specific functionality. For details, see [Implement channel-specific functionality](bot-framework-rest-connector-channeldata.md). ## Suggested actions @@ -55,7 +51,7 @@ The `suggestedActions` property of the [Activity][] object can be used to presen ## Additional resources - [Channels reference][ChannelInspector] -- [Activities overview](https://aka.ms/botSpecs-activitySchema) +- [Activities overview](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) - [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) - [Add media attachments to messages](bot-framework-rest-connector-add-media-attachments.md) - [Add rich cards to messages](bot-framework-rest-connector-add-rich-cards.md) diff --git a/articles/rest-api/bot-framework-rest-connector-quickstart.md b/articles/rest-api/bot-framework-rest-connector-quickstart.md index c419a05a7..d6f60c219 100644 --- a/articles/rest-api/bot-framework-rest-connector-quickstart.md +++ b/articles/rest-api/bot-framework-rest-connector-quickstart.md @@ -1,29 +1,33 @@ --- title: Create a bot with the Bot Connector service - Bot Service -description: Create a bot with the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to create a bot. See how to obtain an access token from the Bot Framework and use the Bot Connector service to exchange messages with users. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: quickstart +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Create a bot with the Bot Connector service +# Create a bot with the Bot Connector service with the Bot Connector API + > [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-quickstart.md) -> - [Node.js](../nodejs/bot-builder-nodejs-quickstart.md) -> - [Bot Service](../bot-service-quickstart.md) +> - [Bot Service SDK](../bot-service-quickstart-create-bot.md) +> - [Bot Service Azure resource](../bot-service-quickstart.md) > - [REST](../rest-api/bot-framework-rest-connector-quickstart.md) -The Bot Connector service enables your bot to exchange messages with channels that are configured in the [Azure Portal](https://portal.azure.com), by using industry-standard REST and JSON over HTTPS. This tutorial walks you through the process of obtaining an access token from the Bot Framework and using the Bot Connector service to exchange messages with the user. +The Bot Connector service enables your bot to exchange messages with channels that are configured in the [Azure portal](https://portal.azure.com), by using industry-standard REST and JSON over HTTPS. This tutorial walks you through the process of obtaining an access token from the Bot Framework and using the Bot Connector service to exchange messages with the user. + + -## Get an access token +## Get an access token > [!IMPORTANT] -> If you have not already done so, you must [register your bot](../bot-service-quickstart-registration.md) with the Bot Framework to obtain its App ID and password. You will need the bot's AppID and password to get an access token. +> If you haven't already done so, you must [register your bot](../bot-service-quickstart-registration.md) with the Bot Framework to obtain its App ID and password. You'll need the bot's AppID and password to get an access token. -To communicate with the Bot Connector service, you must specify an access token in the `Authorization` header of each API request, using this format: +To communicate with the Bot Connector service, you must specify an access token in the `Authorization` header of each API request, using this format: ```http Authorization: Bearer ACCESS_TOKEN @@ -45,7 +49,7 @@ grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT ### Response -If the request succeeds, you will receive an HTTP 200 response that specifies the access token and information about its expiration. +If the request succeeds, you'll receive an HTTP 200 response that specifies the access token and information about its expiration. ```json { @@ -61,18 +65,18 @@ If the request succeeds, you will receive an HTTP 200 response that specifies th ## Exchange messages with the user -A conversation is a series of messages exchanged between a user and your bot. +A conversation is a series of messages exchanged between a user and your bot. ### Receive a message from the user -When the user sends a message, the Bot Framework Connector POSTs a request to the endpoint that you specified when you [registered](../bot-service-quickstart-registration.md) your bot. The body of the request is an [Activity][] object. The following example shows the request body that a bot receives when the user sends a simple message to the bot. +When the user sends a message, the Bot Framework Connector POSTs a request to the endpoint that you specified when you [registered](../bot-service-quickstart-registration.md) your bot. The body of the request is an [Activity][] object. The following example shows the request body that a bot receives when the user sends a simple message to the bot. ```json { "type": "message", "id": "bf3cc9a2f5de...", "timestamp": "2016-10-19T20:17:52.2891902Z", - "serviceUrl": "https://smba.trafficmanager.net/apis", + "serviceUrl": "https://smba.trafficmanager.net/teams", "channelId": "channel's name/id", "from": { "id": "1234abcd", @@ -92,19 +96,19 @@ When the user sends a message, the Bot Framework Connector POSTs a request to th ### Reply to the user's message -When your bot's endpoint receives a `POST` request that represents a message from the user (i.e., `type` = **message**), use the information in that request to create the [Activity][] object for your response. +When your bot's endpoint receives a `POST` request that represents a message from the user (such as, `type` = **message**), use the information in that request to create the [Activity][] object for your response. 1. Set the **conversation** property to the contents of the **conversation** property in the user's message. -2. Set the **from** property to the contents of the **recipient** property in the user's message. -3. Set the **recipient** property to the contents of the **from** property in the user's message. -4. Set the **text** and **attachments** properties as appropriate. +1. Set the **from** property to the contents of the **recipient** property in the user's message. +1. Set the **recipient** property to the contents of the **from** property in the user's message. +1. Set the **text** and **attachments** properties as appropriate. -Use the `serviceUrl` property in the incoming request to [identify the base URI](bot-framework-rest-connector-api-reference.md#base-uri) that your bot should use to issue its response. +Use the `serviceUrl` property in the incoming request to [identify the base URI](bot-framework-rest-connector-api-reference.md#base-uri) that your bot should use to issue its response. -To send the response, `POST` your `Activity` object to `/v3/conversations/{conversationId}/activities/{activityId}`, as shown in the following example. The body of this request is an `Activity` object that prompts the user to select an available appointment time. +To send the response, `POST` your `Activity` object to `/v3/conversations/{conversationId}/activities/{activityId}`, as shown in the following example. The body of this request is an `Activity` object that prompts the user to select an available appointment time. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/bf3cc9a2f5de... +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/bf3cc9a2f5de... Authorization: Bearer eyJhbGciOiJIUzI1Ni... Content-Type: application/json ``` @@ -129,7 +133,7 @@ Content-Type: application/json } ``` -In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). > [!IMPORTANT] > As shown in this example, the `Authorization` header of each API request that you send must contain the word **Bearer** followed by the access token that you [obtained from the Bot Framework](#get-token). @@ -137,7 +141,7 @@ In this example request, `https://smba.trafficmanager.net/apis` represents the b To send another message that enables a user to select an available appointment time by clicking a button, `POST` another request to the same endpoint: ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/bf3cc9a2f5de... +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/bf3cc9a2f5de... Authorization: Bearer eyJhbGciOiJIUzI1Ni... Content-Type: application/json ``` @@ -184,12 +188,12 @@ Content-Type: application/json ], "replyToId": "bf3cc9a2f5de..." } -``` +``` ## Next steps -In this tutorial, you obtained an access token from the Bot Framework and used the Bot Connector service to exchange messages with the user. -You can use the [Bot Framework Emulator](../bot-service-debug-emulator.md) to test and debug your bot. +In this tutorial, you obtained an access token from the Bot Framework and used the Bot Connector service to exchange messages with the user. +You can use the [Bot Framework Emulator](../bot-service-debug-emulator.md) to test and debug your bot. If you'd like to share your bot with others, you'll need to [configure](../bot-service-manage-channels.md) it to run on one or more channels. [Activity]: bot-framework-rest-connector-api-reference.md#activity-object diff --git a/articles/rest-api/bot-framework-rest-connector-send-and-receive-messages.md b/articles/rest-api/bot-framework-rest-connector-send-and-receive-messages.md index b8cc08663..c55326cc6 100644 --- a/articles/rest-api/bot-framework-rest-connector-send-and-receive-messages.md +++ b/articles/rest-api/bot-framework-rest-connector-send-and-receive-messages.md @@ -1,21 +1,25 @@ --- title: Send and receive messages - Bot Service -description: Learn how to send and receive messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to use bots to exchange messages with users. See how to use the Bot Connector service to send messages and replies and to start conversations. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Send and receive messages +# Send and receive messages with the Bot Connector API -The Bot Connector service enables a bot to communicate across multiple channels such as Skype, Email, Slack, and more. It facilitates communication between bot and user, by relaying [activities](https://aka.ms/botSpecs-activitySchema) from bot to channel and from channel to bot. Every activity contains information used for routing the message to the appropriate destination along with information about who created the message, the context of the message, and the recipient of the message. This article describes how to use the Bot Connector service to exchange **message** activities between bot and user on a channel. +The Bot Connector service enables a bot to communicate across multiple channels such as Email, Slack, and more. It facilitates communication between bot and user, by relaying [activities](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) from bot to channel and from channel to bot. Every activity contains information used for routing the message to the appropriate destination along with information about who created the message, the context of the message, and the recipient of the message. This article describes how to use the Bot Connector service to exchange **message** activities between bot and user on a channel. -## Reply to a message + -### Create a reply +## Reply to a message + +### Create a reply When the user sends a message to your bot, your bot will receive the message as an [Activity][] object of type **message**. To create a reply to a user's message, create a new `Activity` object and start by setting these properties: @@ -32,9 +36,9 @@ Next, set the properties that specify the information that you want to communica ### Send the reply -Use the `serviceUrl` property in the incoming activity to [identify the base URI](bot-framework-rest-connector-api-reference.md#base-uri) that your bot should use to issue its response. +Use the `serviceUrl` property in the incoming activity to [identify the base URI](bot-framework-rest-connector-api-reference.md#base-uri) that your bot should use to issue its response. -To send the reply, issue this request: +To send the reply, issue this request: ```http POST /v3/conversations/{conversationId}/activities/{activityId} @@ -42,10 +46,10 @@ POST /v3/conversations/{conversationId}/activities/{activityId} In this request URI, replace **{conversationId}** with the value of the `conversation` object's `id` property within your (reply) Activity and replace **{activityId}** with the value of the `replyToId` property within your (reply) Activity. Set the body of the request to the [Activity][] object that you created to represent your reply. -The following example shows a request that sends a simple text-only reply to a user's message. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a request that sends a simple text-only reply to a user's message. In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -70,34 +74,36 @@ Content-Type: application/json } ``` -## Send a (non-reply) message + + +## Send a (non-reply) message -A majority of the messages that your bot sends will be in reply to messages that it receives from the user. However, there may be times when your bot needs to send a message to the conversation that is not a direct reply to any message from the user. For example, your bot may need to start a new topic of conversation or send a goodbye message at the end of the conversation. +A majority of the messages that your bot sends will be in reply to messages that it receives from the user. However, there may be times when your bot needs to send a message to the conversation that isn't a direct reply to any message from the user. For example, your bot may need to start a new topic of conversation or send a goodbye message at the end of the conversation. -To send a message to a conversation that is not a direct reply to any message from the user, issue this request: +To send a message to a conversation that isn't a direct reply to any message from the user, issue this request: ```http POST /v3/conversations/{conversationId}/activities ``` -In this request URI, replace **{conversationId}** with the ID of the conversation. - +In this request URI, replace **{conversationId}** with the ID of the conversation. + Set the body of the request to an [Activity][] object that you create to represent your reply. > [!NOTE] -> The Bot Framework does not impose any restrictions on the number of messages that a bot may send. -> However, most channels enforce throttling limits to restrict bots from sending a large number of messages in a short period of time. +> The Bot Framework doesn't impose any restrictions on the number of messages that a bot may send. +> However, most channels enforce throttling limits to restrict bots from sending a large number of messages in a short period of time. > Additionally, if the bot sends multiple messages in quick succession, the channel may not always render the messages in the proper sequence. ## Start a conversation -There may be times when your bot needs to initiate a conversation with one or more users. -To start a conversation with a user on a channel, your bot must know its account information and the user's account information on that channel. +There may be times when your bot needs to initiate a conversation with one or more users. +To start a conversation with a user on a channel, your bot must know its account information and the user's account information on that channel. > [!TIP] -> If your bot may need to start conversations with its users in the future, cache user account information, other relevant information such as user preferences and locale, and the service URL (to use as the base URI in the Start Conversation request). +> If your bot may need to start conversations with its users in the future, cache user account information, other relevant information such as user preferences and locale, and the service URL (to use as the base URI in the Start Conversation request). -To start a conversation, issue this request: +To start a conversation, issue this request: ```http POST /v3/conversations @@ -108,10 +114,10 @@ Set the body of the request to a [ConversationParameters][] object that specifie > [!NOTE] > Not all channels support group conversations. Consult the channel's documentation to determine whether a channel supports group conversations and to identify the maximum number of participants that a channel allows in a conversation. -The following example shows a request that starts a conversation. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following example shows a request that starts a conversation. In this example request, `https://smba.trafficmanager.net/teams` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations +POST https://smba.trafficmanager.net/teams/v3/conversations Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -133,7 +139,7 @@ Content-Type: application/json } ``` -If the conversation is established with the specified users, the response will contain an ID that identifies the conversation. +If the conversation is established with the specified users, the response will contain an ID that identifies the conversation. ```json { @@ -146,7 +152,7 @@ Your bot can then use this conversation ID to [send a message](#send-message) to ## Additional resources - [Create messages](bot-framework-rest-connector-create-messages.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) [Activity]: bot-framework-rest-connector-api-reference.md#activity-object [ConversationParameters]: bot-framework-rest-connector-api-reference.md#conversationparameters-object diff --git a/articles/rest-api/bot-framework-rest-connector-text-to-speech.md b/articles/rest-api/bot-framework-rest-connector-text-to-speech.md index a09539745..c00a15cd4 100644 --- a/articles/rest-api/bot-framework-rest-connector-text-to-speech.md +++ b/articles/rest-api/bot-framework-rest-connector-text-to-speech.md @@ -1,31 +1,28 @@ --- title: Add speech to messages - Bot Service -description: Learn how to add speech to messages using the Bot Connector service. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to add speech to bot messages. See how to specify and format the text that bots use on speech-enabled channels and how to include input hints. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Add speech to messages -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-text-to-speech.md) -> - [Node.js](../nodejs/bot-builder-nodejs-text-to-speech.md) -> - [REST](../rest-api/bot-framework-rest-connector-text-to-speech.md) +# Add speech to messages with the Bot Connector API -If you are building a bot for a speech-enabled channel such as Cortana, you can construct messages that specify the text to be spoken by your bot. You can also attempt to influence the state of the client's microphone by specifying an [input hint](bot-framework-rest-connector-add-input-hints.md) to indicate whether your bot is accepting, expecting, or ignoring user input. +If you're building a bot for a speech-enabled channel, you can construct messages that specify the text to be spoken by your bot. You can also attempt to influence the state of the client's microphone by specifying an [input hint](bot-framework-rest-connector-add-input-hints.md) to indicate whether your bot is accepting, expecting, or ignoring user input. ## Specify text to be spoken by your bot -To specify text to be spoken by your bot on a speech-enabled channel, set the `speak` property within the [Activity][Activity] object that represents your message. You can set the `speak` property to either a plain text string or a string that is formatted as Speech Synthesis Markup Language (SSML), an XML-based markup language that enables you to control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, pitch, and more. +To specify text to be spoken by your bot on a speech-enabled channel, set the `speak` property within the [Activity][Activity] object that represents your message. You can set the `speak` property to either a plain text string or a string that is formatted as [Speech Synthesis Markup Language (SSML)](/azure/ai-services/speech-service/speech-synthesis-markup), an XML-based markup language that enables you to control various characteristics of your bot's speech such as voice, rate, volume, pronunciation, pitch, and more. If the channel doesn't support, the message is delivered as text. - -The following request sends a message that specifies text to be displayed and text to be spoken and indicates that the bot is [expecting user input](bot-framework-rest-connector-add-input-hints.md). It specifies the `speak` property using SSML format to indicate that the word "sure" should be spoken with a moderate amount of emphasis. In this example request, `https://smba.trafficmanager.net/apis` represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). +The following request sends a message that specifies text to be displayed and text to be spoken and indicates that the bot is [expecting user input](bot-framework-rest-connector-add-input-hints.md). It specifies the `speak` property using [SSML](/azure/ai-services/speech-service/speech-synthesis-markup) format to indicate that the word "sure" should be spoken with a moderate amount of emphasis. In this example request, Direct Line represents the base URI; the base URI for requests that your bot issues may be different. For details about setting the base URI, see [API Reference](bot-framework-rest-connector-api-reference.md#base-uri). ```http -POST https://smba.trafficmanager.net/apis/v3/conversations/abcd1234/activities/5d5cdc723 +POST https://smba.trafficmanager.net/teams/v3/conversations/abcd1234/activities/5d5cdc723 Authorization: Bearer ACCESS_TOKEN Content-Type: application/json ``` @@ -54,13 +51,13 @@ Content-Type: application/json ## Input hints -When you send a message on a speech-enabled channel, you can attempt to influence the state of the client's microphone by also including an input hint to indicate whether your bot is accepting, expecting, or ignoring user input. For more information, see [Add input hints to messages](bot-framework-rest-connector-add-input-hints.md). +When you send a message on a speech-enabled channel, you can express the intended state of the client's microphone by also including an input hint to indicate whether your bot is accepting, expecting, or ignoring user input. For more information, see [Add input hints to messages](bot-framework-rest-connector-add-input-hints.md). ## Additional resources - [Create messages](bot-framework-rest-connector-create-messages.md) - [Send and receive messages](bot-framework-rest-connector-send-and-receive-messages.md) - [Add input hints to messages](bot-framework-rest-connector-add-input-hints.md) -- Speech Synthesis Markup Language (SSML) +- [Speech Synthesis Markup Language (SSML)](/azure/ai-services/speech-service/speech-synthesis-markup) -[Activity]: bot-framework-rest-connector-api-reference.md#activity-object \ No newline at end of file +[Activity]: bot-framework-rest-connector-api-reference.md#activity-object diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-api-reference.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-api-reference.md index 5ec60dbf8..b47303cb0 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-api-reference.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-api-reference.md @@ -1,18 +1,20 @@ --- title: API reference - Direct Line API 1.1 - Bot Service description: Learn about headers, HTTP status codes, schema, operations, and objects in Direct Line API 1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # API reference - Direct Line API 1.1 > [!IMPORTANT] -> This article contains reference information for Direct Line API 1.1. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-api-reference.md) instead. +> This article contains reference information for Direct Line API 1.1. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-api-reference.md) instead. You can enable your client application to communicate with your bot by using Direct Line API 1.1. Direct Line API 1.1 uses industry-standard REST and JSON over HTTPS. @@ -24,14 +26,16 @@ To access Direct Line API 1.1, use this base URI for all API requests: ## Headers -In addition to the standard HTTP request headers, a Direct Line API request must include an `Authorization` header that specifies a secret or token to authenticate the client that is issuing the request. You can specify the `Authorization` header using either the "Bearer" scheme or the "BotConnector" scheme. +In addition to the standard HTTP request headers, a Direct Line API request must include an `Authorization` header that specifies a secret or token to authenticate the client that is issuing the request. You can specify the `Authorization` header using either the "Bearer" scheme or the "BotConnector" scheme. **Bearer scheme**: + ```http Authorization: Bearer SECRET_OR_TOKEN ``` **BotConnector scheme**: + ```http Authorization: BotConnector SECRET_OR_TOKEN ``` @@ -40,103 +44,118 @@ For details about how to obtain a secret or token that your client can use to au ## HTTP status codes -The HTTP status code that is returned with each response indicates the outcome of the corresponding request. +The [HTTP status code](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) that is returned with each response indicates the outcome of the corresponding request. | HTTP status code | Meaning | |----|----| | 200 | The request succeeded. | | 204 | The request succeeded but no content was returned. | | 400 | The request was malformed or otherwise incorrect. | -| 401 | The client is not authorized to make the request. Often this status code occurs because the `Authorization` header is missing or malformed. | -| 403 | The client is not allowed to perform the requested operation. Often this status code occurs because the `Authorization` header specifies an invalid token or secret. | -| 404 | The requested resource was not found. Typically this status code indicates an invalid request URI. | +| 401 | The client isn't authorized to make the request. Often this status code occurs because the `Authorization` header is missing or malformed. | +| 403 | The client isn't allowed to perform the requested operation. Often this status code occurs because the `Authorization` header specifies an invalid token or secret. | +| 404 | The requested resource wasn't found. Typically this status code indicates an invalid request URI. | | 500 | An internal server error occurred within the Direct Line service | | 502 | A failure occurred within the bot; the bot is unavailable or returned an error. **This is a common error code.** | -## Token operations +## Token operations + Use these operations to create or refresh a token that a client can use to access a single conversation. | Operation | Description | |----|----| -| [Generate Token](#generate-token) | Generate a token for a new conversation. | -| [Refresh Token](#refresh-token) | Refresh a token. | +| [Generate Token](#generate-token) | Generate a token for a new conversation. | +| [Refresh Token](#refresh-token) | Refresh a token. | ### Generate Token -Generates a token that is valid for one conversation. -```http + +Generates a token that is valid for one conversation. + +```http POST /api/tokens/conversation ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | A string that represents the token | +| **Returns** | A string that represents the token | ### Refresh Token -Refreshes the token. -```http + +Refreshes the token. + +```http GET /api/tokens/{conversationId}/renew ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | A string that represents the new token | +| **Returns** | A string that represents the new token | + +## Conversation operations -## Conversation operations Use these operations to open a conversation with your bot and exchange messages between client and bot. | Operation | Description | |----|----| -| [Start Conversation](#start-conversation) | Opens a new conversation with the bot. | +| [Start Conversation](#start-conversation) | Opens a new conversation with the bot. | | [Get Messages](#get-messages) | Retrieves messages from the bot. | -| [Send a Message](#send-a-message) | Sends a message to the bot. | +| [Send a Message](#send-a-message) | Sends a message to the bot. | | [Upload and Send File(s)](#upload-send-files) | Uploads and sends file(s) as attachment(s). | ### Start Conversation -Opens a new conversation with the bot. -```http + +Opens a new conversation with the bot. + +```http POST /api/conversations ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | A [Conversation](#conversation-object) object | +| **Returns** | A [Conversation](#conversation-object) object | ### Get Messages -Retrieves messages from the bot for the specified conversation. Set the `watermark` parameter in the request URI to indicate the most recent message seen by the client. + +Retrieves messages from the bot for the specified conversation. Set the `watermark` parameter in the request URI to indicate the most recent message seen by the client. ```http GET /api/conversations/{conversationId}/messages?watermark={watermark_value} ``` -| | | +| Content | Description | |----|----| | **Request body** | n/a | -| **Returns** | A [MessageSet](#messageset-object) object. The response contains `watermark` as a property of the `MessageSet` object. Clients should page through the available messages by advancing the `watermark` value until no messages are returned. | +| **Returns** | A [MessageSet](#messageset-object) object. The response contains `watermark` as a property of the `MessageSet` object. Clients should page through the available messages by advancing the `watermark` value until no messages are returned. | ### Send a Message -Sends a message to the bot. -```http + +Sends a message to the bot. + +```http POST /api/conversations/{conversationId}/messages ``` -| | | +| Content | Description | |----|----| | **Request body** | A [Message](#message-object) object | | **Returns** | No data is returned in body of the response. The service responds with an HTTP 204 status code if the message was sent successfully. The client may obtain its sent message (along with any messages that the bot has sent to the client) by using the [Get Messages](#get-messages) operation. | -### Upload and Send File(s) + + +### Upload and Send File(s) + Uploads and sends file(s) as attachment(s). Set the `userId` parameter in the request URI to specify the ID of the user that is sending the attachments. -```http + +```http POST /api/conversations/{conversationId}/upload?userId={userId} ``` -| | | +| Content | Description | |----|----| | **Request body** | For a single attachment, populate the request body with the file contents. For multiple attachments, create a multipart request body that contains one part for each attachment, and also (optionally) one part for the [Message](#message-object) object that should serve as the container for the specified attachment(s). For more information, see [Send a message to the bot](bot-framework-rest-direct-line-1-1-send-message.md). | -| **Returns** | No data is returned in body of the response. The service responds with an HTTP 204 status code if the message was sent successfully. The client may obtain its sent message (along with any messages that the bot has sent to the client) by using the [Get Messages](#get-messages) operation. | +| **Returns** | No data is returned in body of the response. The service responds with an HTTP 204 status code if the message was sent successfully. The client may obtain its sent message (along with any messages that the bot has sent to the client) by using the [Get Messages](#get-messages) operation. | > [!NOTE] > Uploaded files are deleted after 24 hours. @@ -151,16 +170,16 @@ Defines a message that a client sends to a bot or receives from a bot. | Property | Type | Description | |----|----|----| -| **id** | string | ID that uniquely identifies the message (assigned by Direct Line). | -| **conversationId** | string | ID that identifies the conversation. | -| **created** | string | Date and time that the message was created, expressed in ISO-8601 format. | -| **from** | string | ID that identifies the user that is the sender of the message. When creating a message, clients should set this property to a stable user ID. Although Direct Line will assign a user ID if none is supplied, this typically results in unexpected behavior. | -| **text** | string | Text of the message that is sent from user to bot or bot to user. | -| **channelData** | object | An object that contains channel-specific content. Some channels provide features that require additional information that cannot be represented using the attachment schema. For those cases, set this property to the channel-specific content as defined in the channel's documentation. This data is sent unmodified between client and bot. This property must either be set to a complex object or left empty. Do not set it to a string, number, or other simple type. | -| **images** | string[] | Array of strings that contains the URL(s) for the image(s) that the message contains. In some cases, strings in this array may be relative URLs. If any string in this array does not begin with either "http" or "https", prepend `https://directline.botframework.com` to the string to form the complete URL. | -| **attachments** | [Attachment](#attachment-object)[] | Array of **Attachment** objects that represent the non-image attachments that the message contains. Each object in the array contains a `url` property and a `contentType` property. In messages that a client receives from a bot, the `url` property may sometimes specify a relative URL. For any `url` property value that does not begin with either "http" or "https", prepend `https://directline.botframework.com` to the string to form the complete URL. | +| **id** | string | ID that uniquely identifies the message (assigned by Direct Line). | +| **conversationId** | string | ID that identifies the conversation. | +| **created** | string | Date and time that the message was created, expressed in the [ISO-8601](https://www.w3.org/QA/Tips/iso-date)) format. | +| **from** | string | ID that identifies the user that is the sender of the message. When creating a message, clients should set this property to a stable user ID. Although Direct Line will assign a user ID if none is supplied, this typically results in unexpected behavior. | +| **text** | string | Text of the message that is sent from user to bot or bot to user. | +| **channelData** | object | An object that contains channel-specific content. Some channels provide features that require additional information that can't be represented using the attachment schema. For those cases, set this property to the channel-specific content as defined in the channel's documentation. This data is sent unmodified between client and bot. This property must either be set to a complex object or left empty. Do not set it to a string, number, or other simple type. | +| **images** | string[] | Array of strings that contains the URL(s) for the image(s) that the message contains. In some cases, strings in this array may be relative URLs. If any string in this array doesn't begin with either "http" or "https", prepend `https://directline.botframework.com` to the string to form the complete URL. | +| **attachments** | [Attachment](#attachment-object)[] | Array of **Attachment** objects that represent the non-image attachments that the message contains. Each object in the array contains a `url` property and a `contentType` property. In messages that a client receives from a bot, the `url` property may sometimes specify a relative URL. For any `url` property value that doesn't begin with either "http" or "https", prepend `https://directline.botframework.com` to the string to form the complete URL. | -The following example shows a Message object that contains all possible properties. In most cases when creating a message, the client only needs to supply the `from` property and at least one content property (e.g., `text`, `images`, `attachments`, or `channelData`). +The following example shows a Message object that contains all possible properties. In most cases when creating a message, the client only needs to supply the `from` property and at least one content property (such as `text`, `images`, `attachments`, or `channelData`). ```json { @@ -188,8 +207,9 @@ The following example shows a Message object that contains all possible properti } ``` -### MessageSet object -Defines a set of messages.

+### MessageSet object + +Defines a set of messages. | Property | Type | Description | |----|----|----| @@ -197,7 +217,8 @@ Defines a set of messages.

| **watermark** | string | Maximum watermark of messages within the set. A client may use the `watermark` value to indicate the most recent message it has seen when [retrieving messages from the bot](bot-framework-rest-direct-line-1-1-receive-messages.md). | ### Attachment object -Defines a non-image attachment.

+ +Defines a non-image attachment. | Property | Type | Description | |----|----|----| @@ -205,7 +226,8 @@ Defines a non-image attachment.

| **url** | string | URL for the content of the attachment. | ### Conversation object -Defines a Direct Line conversation.

+ +Defines a Direct Line conversation. | Property | Type | Description | |----|----|----| @@ -214,7 +236,8 @@ Defines a Direct Line conversation.

| **expires_in** | number | Number of seconds until the token expires. | ### Error object -Defines an error.

+ +Defines an error. | Property | Type | Description | |----|----|----| @@ -223,10 +246,9 @@ Defines an error.

| **statusCode** | number | Status code. | ### ErrorMessage object -A standardized message error payload.

- -| Property | Type | Description | -|------------------------|------------------------|-----------------------------------------------------------------------------| -| error | [Error](#error-object) | An Error object that contains information about the error. | +A standardized message error payload. +| Property | Type | Description | +|--|--|--| +| **error** | [Error](#error-object) | An **Error** object that contains information about the error. | diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-authentication.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-authentication.md index d84789e9d..ecb67f2f6 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-authentication.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-authentication.md @@ -1,60 +1,66 @@ --- title: Direct Line API 1.1 Authentication - Bot Service -description: Learn how to authenticate API requests in Direct Line API v1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn about authentication in version 1.1 of the Direct Line API. See how to use secrets and tokens to access conversations. Find out how to refresh tokens. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Authentication +# Authentication in Direct Line API 1.1 > [!IMPORTANT] -> This article describes authentication in Direct Line API 1.1. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-authentication.md) instead. +> This article describes authentication in Direct Line API 1.1. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-authentication.md) instead. -A client can authenticate requests to Direct Line API 1.1 either by using a **secret** that you [obtain from the Direct Line channel configuration page](../bot-service-channel-connect-directline.md) in the Bot Framework Portal or by using a **token** that you obtain at runtime. +A client can authenticate requests to Direct Line API 1.1 either by using a *secret* that you [obtain from the Direct Line channel configuration page](../bot-service-channel-connect-directline.md) in the Bot Framework Portal or by using a *token* that you obtain at runtime. -The secret or token should be specified in the `Authorization` header of each request, using either the "Bearer" scheme or the "BotConnector" scheme. +The secret or token should be specified in the `Authorization` header of each request, using either the "Bearer" scheme or the "BotConnector" scheme. **Bearer scheme**: + ```http Authorization: Bearer SECRET_OR_TOKEN ``` **BotConnector scheme**: + ```http Authorization: BotConnector SECRET_OR_TOKEN ``` ## Secrets and tokens -A Direct Line **secret** is a master key that can be used to access any conversation that belongs to the associated bot. A **secret** can also be used to obtain a **token**. Secrets do not expire. +A Direct Line *secret* is a master key that can be used to access any conversation that belongs to the associated bot. A *secret* can also be used to obtain a *token*. Secrets don't expire. -A Direct Line **token** is a key that can be used to access a single conversation. A token expires but can be refreshed. +A Direct Line *token* is a key that can be used to access a single conversation. A token expires but can be refreshed. -If you're creating a service-to-service application, specifying the **secret** in the `Authorization` header of Direct Line API requests may be simplest approach. If you're writing an application where the client runs in a web browser or mobile app, you may want to exchange your secret for a token (which only works for a single conversation and will expire unless refreshed) and specify the **token** in the `Authorization` header of Direct Line API requests. Choose the security model that works best for you. +If you're creating a service-to-service application, specifying the *secret* in the `Authorization` header of Direct Line API requests may be simplest approach. If you're writing an application where the client runs in a web browser or mobile app, you may want to exchange your secret for a token (which only works for a single conversation and will expire unless refreshed) and specify the *token* in the `Authorization` header of Direct Line API requests. Choose the security model that works best for you. > [!NOTE] -> Your Direct Line client credentials are different from your bot's credentials. This enables you to revise your keys independently and lets you share client tokens without disclosing your bot's password. +> Your Direct Line client credentials are different from your bot's credentials. This enables you to revise your keys independently and lets you share client tokens without disclosing your bot's password. ## Get a Direct Line secret -You can [obtain a Direct Line secret](../bot-service-channel-connect-directline.md) via the Direct Line channel configuration page for your bot in the [Azure Portal](https://portal.azure.com): +You can [obtain a Direct Line secret](../bot-service-channel-connect-directline.md) via the Direct Line channel configuration page for your bot in the [Azure portal](https://portal.azure.com): -![Direct Line configuration](../media/direct-line-configure.png) +:::image type="content" source="../media/direct-line-configure.png" alt-text="Screenshot of configuration settings for a Direct Line site."::: -## Generate a Direct Line token + -To generate a Direct Line token that can be used to access a single conversation, first obtain the Direct Line secret from the Direct Line channel configuration page in the [Azure Portal](https://portal.azure.com). Then issue this request to exchange your Direct Line secret for a Direct Line token: +## Generate a Direct Line token + +To generate a Direct Line token that can be used to access a single conversation, first obtain the Direct Line secret from the Direct Line channel configuration page in the [Azure portal](https://portal.azure.com). Then issue this request to exchange your Direct Line secret for a Direct Line token: ```http POST https://directline.botframework.com/api/tokens/conversation Authorization: Bearer SECRET ``` -In the `Authorization` header of this request, replace **SECRET** with the value of your Direct Line secret. +In the `Authorization` header of this request, replace *SECRET* with the value of your Direct Line secret. The following snippets provide an example of the Generate Token request and response. @@ -78,20 +84,22 @@ HTTP/1.1 200 OK ### Generate Token versus Start Conversation -The Generate Token operation (`POST /api/tokens/conversation`) is similar to the [Start Conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) operation (`POST /api/conversations`) in that both operations return a `token` that can be used to access a single conversation. However, unlike the Start Conversation operation, the Generate Token operation does not start the conversation or contact the bot. +The Generate Token operation (`POST /api/tokens/conversation`) is similar to the [Start Conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) operation (`POST /api/conversations`) in that both operations return a `token` that can be used to access a single conversation. However, unlike the Start Conversation operation, the Generate Token operation doesn't start the conversation or contact the bot. If you plan to distribute the token to clients and want them to initiate the conversation, use the Generate Token operation. If you intend to start the conversation immediately, use the [Start Conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) operation instead. -## Refresh a Direct Line token + + +## Refresh a Direct Line token -A Direct Line token is valid for 30 minutes from the time it is generated and can be refreshed an unlimited amount of times, as long as it has not expired. An expired token cannot be refreshed. To refresh a Direct Line token, issue this request: +A Direct Line token is valid for 30 minutes from the time it's generated and can be refreshed an unlimited number of times, as long as it hasn't expired. An expired token can't be refreshed. To refresh a Direct Line token, issue this request: ```http POST https://directline.botframework.com/api/tokens/{conversationId}/renew Authorization: Bearer TOKEN_TO_BE_REFRESHED ``` -In the URI of this request, replace **{conversationId}** with the ID of the conversation for which the token is valid and in the `Authorization` header of this request, replace **TOKEN_TO_BE_REFRESHED** with the Direct Line token that you want to refresh. +In the URI of this request, replace *{conversationId}* with the ID of the conversation for which the token is valid and in the `Authorization` header of this request, replace *TOKEN_TO_BE_REFRESHED* with the Direct Line token that you want to refresh. The following snippets provide an example of the Refresh Token request and response. @@ -116,4 +124,4 @@ HTTP/1.1 200 OK ## Additional resources - [Key concepts](bot-framework-rest-direct-line-1-1-concepts.md) -- [Connect a bot to Direct Line](../bot-service-channel-connect-directline.md) \ No newline at end of file +- [Connect a bot to Direct Line](../bot-service-channel-connect-directline.md) diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-concepts.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-concepts.md index e8315aa69..96a66cf60 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-concepts.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-concepts.md @@ -1,24 +1,26 @@ --- title: Key concepts in the Bot Framework Direct Line API 1.1 - Bot Service -description: Understand key concepts in the Bot Framework Direct Line API 1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn about version 1.1 of the Bot Framework Direct Line API. View information on authentication, starting conversations, messages, and developer resources. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Key concepts in Direct Line API 1.1 -You can enable communication between your bot and your own client application by using the Direct Line API. +You can enable communication between your bot and your own client application by using the Direct Line API. > [!IMPORTANT] -> This article introduces key concepts in Direct Line API 1.1 and provides information about relevant developer resources. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-concepts.md) instead. +> This article introduces key concepts in Direct Line API 1.1 and provides information about relevant developer resources. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-concepts.md) instead. ## Authentication -Direct Line API 1.1 requests can be authenticated either by using a **secret** that you obtain from the Direct Line channel configuration page in the [Azure Portal](https://portal.azure.com) or by using a **token** that you obtain at runtime. For more information, see [Authentication](bot-framework-rest-direct-line-1-1-authentication.md). +Direct Line API 1.1 requests can be authenticated either by using a *secret* that you obtain from the Direct Line channel configuration page in the [Azure portal](https://portal.azure.com) or by using a *token* that you obtain at runtime. For more information, see [Authentication](bot-framework-rest-direct-line-1-1-authentication.md). ## Starting a conversation @@ -36,10 +38,6 @@ Using Direct Line API 1.1, a client can receive messages by polling with `HTTP G ### Client library -The Bot Framework provides a client library that facilitates access to Direct Line API 1.1 via C#. To use the client library within a Visual Studio project, install the `Microsoft.Bot.Connector.DirectLine` v1.x NuGet package. +The Bot Framework provides a client library that facilitates access to Direct Line API 1.1 via C#. To use the client library within a Visual Studio project, install the `Microsoft.Bot.Connector.DirectLine` [v1.x NuGet package](https://www.nuget.org/packages/Microsoft.Bot.Connector.DirectLine/1.1.1). -As an alternative to using the C# client library, you can generate your own client library in the language of your choice by using the Direct Line API 1.1 Swagger file. - -### Web chat control - -The Bot Framework provides a control that enables you to embed a Direct-Line-powered bot into your client application. For more information, see the Microsoft Bot Framework WebChat control. +As an alternative to using the C# client library, you can generate your own client library in the language of your choice by using the [Direct Line API 1.1 Swagger file](https://docs.botframework.com/restapi/directline/swagger.json). diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md index 517dc6a8c..8f829e5d6 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-receive-messages.md @@ -1,26 +1,28 @@ --- title: Receive messages from the bot - Bot Service -description: Learn how to receive messages from the bot using Direct Line API v1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to use version 1.1 of the Direct Line API to receive messages from bots. See how to issue GET requests. Become familiar with timing considerations. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Receive messages from the bot +# Receive messages from the bot in Direct Line API 1.1 > [!IMPORTANT] -> This article describes how to receive messages from the bot using Direct Line API 1.1. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-receive-activities.md) instead. +> This article describes how to receive messages from the bot using Direct Line API 1.1. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-receive-activities.md) instead. -Using the Direct Line 1.1 protocol, clients must poll an `HTTP GET` interface to receive messages. +Using the Direct Line 1.1 protocol, clients must poll an `HTTP GET` interface to receive messages. ## Retrieve messages with HTTP GET To retrieve messages for a specific conversation, issue a `GET` request to the `api/conversations/{conversationId}/messages` endpoint, optionally specifying the `watermark` parameter to indicate the most recent message seen by the client. An updated `watermark` value will be returned in the JSON response, even if no messages are included. -The following snippets provide an example of the Get Messages request and response. The Get Messages response contains `watermark` as a property of the [MessageSet](bot-framework-rest-direct-line-1-1-api-reference.md#messageset-object). Clients should page through the available messages by advancing the `watermark` value until no messages are returned. +The following snippets provide an example of the Get Messages request and response. The Get Messages response contains `watermark` as a property of the [MessageSet](bot-framework-rest-direct-line-1-1-api-reference.md#messageset-object). Clients should page through the available messages by advancing the `watermark` value until no messages are returned. ### Request @@ -58,7 +60,7 @@ HTTP/1.1 200 OK ## Timing considerations -Even though Direct Line is a multi-part protocol with potential timing gaps, the protocol and service is designed to make it easy to build a reliable client. The `watermark` property that is sent in the Get Messages response is reliable. A client will not miss any messages as long as it replays the watermark verbatim. +Even though Direct Line is a multi-part protocol with potential timing gaps, the protocol and service is designed to make it easy to build a reliable client. The `watermark` property that is sent in the Get Messages response is reliable. A client won't miss any messages as long as it replays the watermark verbatim. Clients should choose a polling interval that matches their intended use. @@ -71,4 +73,4 @@ Clients should choose a polling interval that matches their intended use. - [Key concepts](bot-framework-rest-direct-line-1-1-concepts.md) - [Authentication](bot-framework-rest-direct-line-1-1-authentication.md) - [Start a conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) -- [Send a message to the bot](bot-framework-rest-direct-line-1-1-send-message.md) \ No newline at end of file +- [Send a message to the bot](bot-framework-rest-direct-line-1-1-send-message.md) diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-send-message.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-send-message.md index 5f0bc26b7..1189dd4b5 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-send-message.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-send-message.md @@ -1,20 +1,22 @@ --- title: Send a message the bot - Bot Service -description: Learn how to send a message to the bot using Direct Line API v1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Find out how to use version 1.1 of the Direct Line API to send messages to bots. Learn how to create and send messages and attachments. See expected responses. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Send a message to the bot +# Send a message to the bot in Direct Line API 1.1 > [!IMPORTANT] -> This article describes how to send a message to the bot using Direct Line API 1.1. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-send-activity.md) instead. +> This article describes how to send a message to the bot using Direct Line API 1.1. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-send-activity.md) instead. -Using the Direct Line 1.1 protocol, clients can exchange messages with bots. These messages are converted to the schema that the bot supports (Bot Framework v1 or Bot Framework v3). A client may send a single message per request. +Using the Direct Line 1.1 protocol, clients can exchange messages with bots. These messages are converted to the schema that the bot supports (Bot Framework v1 or Bot Framework v3). A client may send a single message per request. ## Send a message @@ -39,7 +41,7 @@ Authorization: Bearer RCurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCp ### Response -When the message is delivered to the bot, the service responds with an HTTP status code that reflects the bot's status code. If the bot generates an error, an HTTP 500 response ("Internal Server Error") is returned to the client in response to its Send Message request. If the POST is successful, the service returns an HTTP 204 status code. No data is returned in body of the response. The client's message and any messages from the bot can be obtained via [polling](bot-framework-rest-direct-line-1-1-receive-messages.md). +When the message is delivered to the bot, the service responds with an HTTP status code that reflects the bot's status code. If the bot generates an error, an HTTP 500 response ("Internal Server Error") is returned to the client in response to its Send Message request. If the POST is successful, the service returns an HTTP 204 status code. No data is returned in body of the response. The client's message and any messages from the bot can be obtained via [polling](bot-framework-rest-direct-line-1-1-receive-messages.md). ```http HTTP/1.1 204 No Content @@ -60,17 +62,23 @@ The total time to POST a message to a Direct Line conversation is the sum of the In some situations, a client may need to send attachments to the bot such as images or documents. A client may send attachments to the bot either by [specifying the URL(s)](#send-by-url) of the attachment(s) within the [Message](bot-framework-rest-direct-line-1-1-api-reference.md#message-object) object that it sends using `POST /api/conversations/{conversationId}/messages` or by [uploading attachment(s)](#upload-attachments) using `POST /api/conversations/{conversationId}/upload`. -## Send attachment(s) by URL + + +## Send attachment(s) by URL To send one or more attachments as part of the [Message](bot-framework-rest-direct-line-1-1-api-reference.md#message-object) object using `POST /api/conversations/{conversationId}/messages`, specify the attachment URL(s) within the message's `images` array and/or `attachments` array. -## Send attachment(s) by upload + + +## Send attachment(s) by upload Often, a client may have image(s) or document(s) on a device that it wants to send to the bot, but no URLs corresponding to those files. In this situation, a client can can issue a `POST /api/conversations/{conversationId}/upload` request to send attachments to the bot by upload. The format and contents of the request will depend upon whether the client is [sending a single attachment](#upload-one-attachment) or [sending multiple attachments](#upload-multiple-attachments). -### Send a single attachment by upload + -To send a single attachment by upload, issue this request: +### Send a single attachment by upload + +To send a single attachment by upload, issue this request: ```http POST https://directline.botframework.com/api/conversations/{conversationId}/upload?userId={userId} @@ -107,11 +115,13 @@ HTTP/1.1 204 No Content [other headers] ``` -### Send multiple attachments by upload + + +### Send multiple attachments by upload -To send multiple attachments by upload, `POST` a multipart request to the `/api/conversations/{conversationId}/upload` endpoint. Set the `Content-Type` header of the request to `multipart/form-data` and include the `Content-Type` header and `Content-Disposition` header for each part to specify each attachment's type and filename. In the request URI, set the `userId` parameter to the ID of the user that is sending the message. +To send multiple attachments by upload, `POST` a multipart request to the `/api/conversations/{conversationId}/upload` endpoint. Set the `Content-Type` header of the request to `multipart/form-data` and include the `Content-Type` header and `Content-Disposition` header for each part to specify each attachment's type and filename. In the request URI, set the `userId` parameter to the ID of the user that is sending the message. -You may include a [Message](bot-framework-rest-direct-line-1-1-api-reference.md#message-object) object within the request by adding a part that specifies the `Content-Type` header value `application/vnd.microsoft.bot.message`. This allows the client to customize the message that contains the attachment(s). If the request includes a Message, the attachments that are specified by other parts of the payload are added as attachments to that Message before it is sent. +You may include a [Message](bot-framework-rest-direct-line-1-1-api-reference.md#message-object) object within the request by adding a part that specifies the `Content-Type` header value `application/vnd.microsoft.bot.message`. This allows the client to customize the message that contains the attachment(s). If the request includes a Message, the attachments that are specified by other parts of the payload are added as attachments to that Message before it's sent. The following snippets provide an example of the Send (multiple) Attachments request and response. In this example, the request sends a message that contains some text and a single image attachment. Additional parts could be added to the request to include multiple attachments in this message. @@ -156,4 +166,4 @@ HTTP/1.1 204 No Content - [Key concepts](bot-framework-rest-direct-line-1-1-concepts.md) - [Authentication](bot-framework-rest-direct-line-1-1-authentication.md) - [Start a conversation](bot-framework-rest-direct-line-1-1-start-conversation.md) -- [Receive messages from the bot](bot-framework-rest-direct-line-1-1-receive-messages.md) \ No newline at end of file +- [Receive messages from the bot](bot-framework-rest-direct-line-1-1-receive-messages.md) diff --git a/articles/rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md b/articles/rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md index 7ae924e2a..6ab578c6a 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md +++ b/articles/rest-api/bot-framework-rest-direct-line-1-1-start-conversation.md @@ -1,18 +1,20 @@ --- title: Start a conversation using Direct Line API 1.1 - Bot Service -description: Learn how to start a conversation using Direct Line API v1.1. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to use version 1.1 of the Direct Line API to start conversations with bots. Find out how the Start Conversation and Generate Token operations differ. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Start a conversation +# Start a conversation in Direct Line API 1.1 > [!IMPORTANT] -> This article describes how to start a conversation using Direct Line API 1.1. If you are creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-start-conversation.md) instead. +> This article describes how to start a conversation using Direct Line API 1.1. If you're creating a new connection between your client application and bot, use [Direct Line API 3.0](bot-framework-rest-direct-line-3-0-start-conversation.md) instead. Direct Line conversations are explicitly opened by clients and may run as long as the bot and client participate and have valid credentials. While the conversation is open, both the bot and client may send messages. More than one client may connect to a given conversation and each client may participate on behalf of multiple users. @@ -53,11 +55,11 @@ HTTP/1.1 200 OK ## Start Conversation versus Generate Token -The Start Conversation operation (`POST /api/conversations`) is similar to the [Generate Token](bot-framework-rest-direct-line-1-1-authentication.md#generate-token) operation (`POST /api/tokens/conversation`) in that both operations return a `token` that can be used to access a single conversation. However, the Start Conversation operation also starts the conversation and contacts the bot, whereas the Generate Token operation does neither of these things. +The Start Conversation operation (`POST /api/conversations`) is similar to the [Generate Token](bot-framework-rest-direct-line-1-1-authentication.md#generate-token) operation (`POST /api/tokens/conversation`) in that both operations return a `token` that can be used to access a single conversation. However, the Start Conversation operation also starts the conversation and contacts the bot, whereas the Generate Token operation does neither of these things. -If you intend to start the conversation immediately, use the Start Conversation operation. If you plan to distribute the token to clients and want them to initiate the conversation, use the [Generate Token](bot-framework-rest-direct-line-1-1-authentication.md#generate-token) operation instead. +If you intend to start the conversation immediately, use the Start Conversation operation. If you plan to distribute the token to clients and want them to initiate the conversation, use the [Generate Token](bot-framework-rest-direct-line-1-1-authentication.md#generate-token) operation instead. ## Additional resources - [Key concepts](bot-framework-rest-direct-line-1-1-concepts.md) -- [Authentication](bot-framework-rest-direct-line-1-1-authentication.md) \ No newline at end of file +- [Authentication](bot-framework-rest-direct-line-1-1-authentication.md) diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-api-reference.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-api-reference.md index a8b1ae539..f44b7805a 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-api-reference.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-api-reference.md @@ -1,12 +1,14 @@ --- -title: API reference - Direct Line API 3.0 - Bot Service +title: API reference - Direct Line API 3.0 description: Learn about headers, HTTP status codes, schema, operations, and objects in Direct Line API 3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # API reference - Direct Line API 3.0 @@ -15,9 +17,18 @@ You can enable your client application to communicate with your bot by using Dir ## Base URI -To access Direct Line API 3.0, use this base URI for all API requests: +To access Direct Line API 3.0, use one of these base URIs for all API requests: -`https://directline.botframework.com` +- For global bots, use `https://directline.botframework.com` +- For a regional bot, enter following uri according to the selected region: + + | Region | Base URI | + |:-|:-| + | Europe| `https://europe.directline.botframework.com` | + | India | `https://india.directline.botframework.com` | + +> [!TIP] +> A request might fail if you use the global base URI for a regional bot, as some requests could go beyond geographical boundaries. ## Headers @@ -31,7 +42,7 @@ For details about how to obtain a secret or token that your client can use to au ## HTTP status codes -The HTTP status code that is returned with each response indicates the outcome of the corresponding request. +The [HTTP status code](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) that is returned with each response indicates the outcome of the corresponding request. | HTTP status code | Meaning | |----|----| @@ -40,9 +51,9 @@ The Generate a Direct Line token + -To generate a Direct Line token that can be used to access a single conversation, first obtain the Direct Line secret from the Direct Line channel configuration page in the [Azure Portal](https://portal.azure.com). Then issue this request to exchange your Direct Line secret for a Direct Line token: +## Generate a Direct Line token + +To generate a Direct Line token that can be used to access a single conversation, first obtain the Direct Line secret from the Direct Line channel configuration page in the [Azure portal](https://portal.azure.com). Then issue this request to exchange your Direct Line secret for a Direct Line token: ```http POST https://directline.botframework.com/v3/directline/tokens/generate @@ -59,7 +63,7 @@ POST https://directline.botframework.com/v3/directline/tokens/generate Authorization: Bearer RCurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0 ``` -The request payload, which contains the token parameters, is optional but recommended. When generating a token that can be sent back to the Direct Line service, provide the following payload to make the connection more secure. By including these values, Direct Line can perform additional security validation of the user ID and name, inhibiting tampering of these values by malicious clients. Including these values also improves Direct Line's ability to send the _conversation update_ activity, allowing it to generate the conversation update immediately upon the user joining the conversation. When this information is not provided, the user must send content before Direct Line can send the conversation update. +The request payload, which contains the token parameters, is optional but recommended. When generating a token that can be sent back to the Direct Line service, provide the following payload to make the connection more secure. By including these values, Direct Line can perform additional security validation of the user ID and name, inhibiting tampering of these values by malicious clients. Including these values also improves Direct Line's ability to send the _conversation update_ activity, allowing it to generate the conversation update immediately upon the user joining the conversation. When this information isn't provided, the user must send content before Direct Line can send the conversation update. ```json { @@ -98,13 +102,15 @@ HTTP/1.1 200 OK ### Generate Token versus Start Conversation -The Generate Token operation (`POST /v3/directline/tokens/generate`) is similar to the [Start Conversation](bot-framework-rest-direct-line-3-0-start-conversation.md) operation (`POST /v3/directline/conversations`) in that both operations return a `token` that can be used to access a single conversation. However, unlike the Start Conversation operation, the Generate Token operation does not start the conversation, does not contact the bot, and does not create a streaming WebSocket URL. +The Generate Token operation (`POST /v3/directline/tokens/generate`) is similar to the [Start Conversation](bot-framework-rest-direct-line-3-0-start-conversation.md) operation (`POST /v3/directline/conversations`) in that both operations return a `token` that can be used to access a single conversation. However, unlike the Start Conversation operation, the Generate Token operation doesn't start the conversation, doesn't contact the bot, and doesn't create a streaming WebSocket URL. If you plan to distribute the token to clients and want them to initiate the conversation, use the Generate Token operation. If you intend to start the conversation immediately, use the [Start Conversation](bot-framework-rest-direct-line-3-0-start-conversation.md) operation instead. -## Refresh a Direct Line token + + +## Refresh a Direct Line token -A Direct Line token can be refreshed an unlimited amount of times, as long as it has not expired. An expired token cannot be refreshed. To refresh a Direct Line token, issue this request: +A Direct Line token can be refreshed an unlimited number of times, as long as it hasn't expired. An expired token can't be refreshed. To refresh a Direct Line token, issue this request: ```http POST https://directline.botframework.com/v3/directline/tokens/refresh @@ -139,43 +145,41 @@ HTTP/1.1 200 OK } ``` -## Azure Bot Service authentication +## Azure AI Bot Service authentication -The information presented in this section is based on the [Add authentication to your bot via Azure Bot Service](../v4sdk/bot-builder-authentication.md) article. +The information presented in this section is based on the [Add authentication to your bot via Azure AI Bot Service](../v4sdk/bot-builder-authentication.md) article. -**Azure Bot Service authentication** enables you to authenticate users to and get **access tokens** from a variety of identity providers such as *Azure Active Directory*, *GitHub*, *Uber* and so on. You can also configure authentication for a custom **OAuth2** identity provider. All this enables you to write **one piece of authentication code** that works across all supported identity providers and channels. To utilize these capabilities you need to perform the following steps: +**Azure AI Bot Service authentication** enables you to authenticate users to and get **access tokens** from various identity providers such as _Microsoft Entra ID_, _GitHub_, _Uber_ and so on. You can also configure authentication for a custom **OAuth2** identity provider. All this enables you to write **one piece of authentication code** that works across all supported identity providers and channels. To use these capabilities: 1. Statically configure `settings` on your bot that contains the details of your application registration with an identity provider. -2. Use an `OAuthCard`, backed by the application information you supplied in the previous step, to sign-in a user. -3. Retrieve access tokens through **Azure Bot Service API**. +1. Use an `OAuthCard`, backed by the application information you supplied in the previous step, to sign-in a user. +1. Retrieve access tokens through **Azure AI Bot Service API**. ### Security considerations -When you use *Azure Bot Service authentication* with [Web Chat](../bot-service-channel-connect-webchat.md) there are some important security considerations you must keep in mind. +When you use _Azure AI Bot Service authentication_ with [Web Chat](../bot-service-channel-connect-webchat.md), there are some important security considerations you must keep in mind. -1. **Impersonation**. Impersonation here means an attacker makes the bot thinks he is someone else. In Web Chat, an attacker can impersonate someone else by **changing the user ID** of his Web Chat instance. To prevent this, it is recommend to bot developers to make the **user ID unguessable**. +1. **Impersonation**. Impersonation is when an attacker convinces the bot that the attacker is someone else. In Web Chat, an attacker can impersonate someone else by _changing the user ID_ of their Web Chat instance. To prevent impersonation, we recommend that bot developers make the _user ID unguessable_. - If you enable **enhanced authentication** options, Azure Bot Service can further detect and reject any user ID change. This means the user ID (`Activity.From.Id`) on messages from Direct Line to your bot will always be the same as the one you initialized the Web Chat with. Note that this feature requires the user ID starts with `dl_`. + If you enable **enhanced authentication** options, Azure AI Bot Service can further detect and reject any user ID change. This means the user ID (`Activity.From.Id`) on messages from Direct Line to your bot will always be the same as the one you initialized the Web Chat with. This feature requires the user ID to start with `dl_`. > [!NOTE] - > When a *User.Id* is provided while exchanging a secret for a token, that *User.Id* is embedded in the token. DirectLine males sure the messages sent to the bot have that id as the activity's *From.Id*. If a client sends a message to DirectLine having a different *From.Id*, it will be changed to the **Id in the token** before forwarding the message to the bot. So you cannot use another user id after a channel secret is initialized with a user id + > When a _User.Id_ is provided while exchanging a secret for a token, that _User.Id_ is embedded in the token. Direct Line makes sure the messages sent to the bot have that ID as the activity's _From.Id_. If a client sends a message to Direct Line having a different _From.Id_, it will be changed to the _ID in the token_ before forwarding the message to the bot. So you can't use another user ID after a channel secret is initialized with a user ID -1. **User identities**. You must be aware that your are dealing with two user identities: +1. **User identities**. Each user has multiple user identities: - 1. The user’s identity in a channel. - 1. The user’s identity in an identity provider that the bot is interested in. + 1. The user's identity in a channel. + 1. The user's identity in an identity provider that the bot is interested in. - When a bot asks user A in a channel to sign-in to an identity provider P, the sign-in process must assure that user A is the one that signs into P. - If another user B is allowed to sign-in, then user A would have access to user B’s resource through the bot. In Web Chat we have 2 mechanisms for ensuring the right user signed in as described next. +When a bot asks user A in a channel to sign-in to an identity provider P, the sign-in process must assure that user A is the one that signs into P. If another user B is allowed to sign-in, then user A would have access to user B's resource through the bot. In Web Chat, we have two mechanisms for ensuring the right user signed in as described next. - 1. At the end of sign-in, in the past, the user was presented with a randomly generated 6-digit code (aka magic code). The user must type this code in the conversation that initiated the sign-in to complete the sign-in process. This mechanism tends to result in a bad user experience. Additionally, it is still susceptible to phishing attacks. A malicious user can trick another user to sign-in and obtain the magic code through phishing. +1. At the end of sign-in, in the past, the user was presented with a randomly generated 6-digit code (magic code). The user must type this code in the conversation that initiated the sign-in to complete the sign-in process. This mechanism tends to result in a bad user experience. Additionally, it's still susceptible to phishing attacks. A malicious user can trick another user to sign-in and obtain the magic code through phishing. - 2. Because of the issues with the previous approach, Azure Bot Service removed the need for the magic code. Azure Bot Service guarantees that the sign-in process can only be completed in the **same browser session** as the Web Chat itself. - To enable this protection, as a bot developer, you must start Web Chat with a **Direct Line token** that contains a **list of trusted domains that can host the bot’s Web Chat client**. Before, you could only obtain this token by passing an undocumented optional parameter to the Direct Line token API. Now, with enhanced authentication options, you can statically specify the trusted domain (origin) list in the Direct Line configuration page. +1. Because of the issues with the previous approach, Azure AI Bot Service removed the need for the magic code. Azure AI Bot Service guarantees that the sign-in process can only be completed in the **same browser session** as the Web Chat itself. To enable this protection, as a bot developer, you must start Web Chat with a **Direct Line token** that contains a **list of trusted domains that can host the bot's Web Chat client**. Before, you could only obtain this token by passing an undocumented optional parameter to the Direct Line token API. Now, with enhanced authentication options, you can statically specify the trusted domain (origin) list in the Direct Line configuration page. - See also [Add authentication to your bot via Azure Bot Service](../v4sdk/bot-builder-authentication.md). +For more information, see [Add authentication to your bot via Azure AI Bot Service](../v4sdk/bot-builder-authentication.md). ### Code examples @@ -272,8 +276,8 @@ router.get('/config', function(req, res) { ``` -## Additional resources +## Additional information - [Key concepts](bot-framework-rest-direct-line-3-0-concepts.md) - [Connect a bot to Direct Line](../bot-service-channel-connect-directline.md) -- [Add authentication to your bot via Azure Bot Service](../bot-builder-tutorial-authentication.md) +- [Add authentication to your bot via Azure AI Bot Service](../bot-builder-tutorial-authentication.md) diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-concepts.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-concepts.md index fdb10b574..cf1c32140 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-concepts.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-concepts.md @@ -1,21 +1,23 @@ --- -title: Key concepts in the Bot Framework Direct Line API 3.0 - Bot Service -description: Understand key concepts in the Bot Framework Direct Line API 3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/06/2019 +title: Key concepts in the Bot Framework Direct Line API 3.0 - Azure AI Bot Service +description: Learn about version 3.0 of the Bot Framework Direct Line API. View information on authentication, starting conversations, messages, and developer resources. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Key concepts in Direct Line API 3.0 -You can enable communication between your bot and your own client application by using the Direct Line API. This article introduces key concepts in Direct Line API 3.0 and provides information about relevant developer resources. +You can enable communication between your bot and your own client application by using the Direct Line API. This article introduces key concepts in Direct Line API 3.0 and provides information about relevant developer resources. You can build a client using the SDK, REST API, or Web Chat. ## Authentication -Direct Line API 3.0 requests can be authenticated either by using a **secret** that you obtain from the Direct Line channel configuration page in the [Azure Portal](https://portal.azure.com) or by using a **token** that you obtain at runtime. For more information, see [Authentication](bot-framework-rest-direct-line-3-0-authentication.md). +Direct Line API 3.0 requests can be authenticated either by using a **secret** that you obtain from the Direct Line channel configuration page in the [Azure portal](https://portal.azure.com) or by using a **token** that you obtain at runtime. For more information, see [Authentication](bot-framework-rest-direct-line-3-0-authentication.md). ## Starting a conversation @@ -33,27 +35,12 @@ Using Direct Line API 3.0, a client can receive messages from your bot either vi ### Client libraries -The Bot Framework provides client libraries that facilitate access to Direct Line API 3.0 via C# and Node.js. +The Bot Framework provides client libraries that facilitate access to Direct Line API 3.0 via C# and Node.js. -- To use the .NET client library within a Visual Studio project, install the `Microsoft.Bot.Connector.DirectLine` NuGet package. +- To use the .NET client library within a Visual Studio project, install the `Microsoft.Bot.Connector.DirectLine` [NuGet package](https://www.nuget.org/packages/Microsoft.Bot.Connector.DirectLine).s -- To use the Node.js client library, install the `botframework-directlinejs` library using NPM (or download the source). +- To use the Node.js client library, install the `botframework-directlinejs` library using [NPM](https://www.npmjs.com/package/botframework-directlinejs) (or download the [source](https://github.com/Microsoft/BotFramework-DirectLineJS)). -::: moniker range="azure-bot-service-3.0" +### Web chat control -### Sample code - -The BotBuilder-Samples GitHub repo contains multiple samples that show how to use Direct Line API 3.0 with C# and Node.js. - -| Sample | Language | Description | -|----|----|----| -| Direct Line Bot Sample | C# | A sample bot and a custom client communicating to each other using the Direct Line API. | -| Direct Line Bot Sample (using client WebSockets) | C# | A sample bot and a custom client communicating to each other using the Direct Line API and WebSockets. | -| Direct Line Bot Sample | JavaScript | A sample bot and a custom client communicating to each other using the Direct Line API. | -| Direct Line Bot Sample (using client WebSockets) | JavaScript | A sample bot and a custom client communicating to each other using the Direct Line API and WebSockets. | - -::: moniker-end - -### Web chat control - -The Bot Framework provides a control that enables you to embed a Direct-Line-powered bot into your client application. For more information, see the Microsoft Bot Framework WebChat control. +The Bot Framework provides a control that enables you to embed a Direct-Line-powered bot into your client application. For more information, see the [Microsoft Bot Framework WebChat control](https://github.com/Microsoft/BotFramework-WebChat). diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md index 5b81add46..ae288eae8 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-end-conversation.md @@ -1,26 +1,26 @@ --- -title: End a conversation - Bot Service -description: Learn how to end a conversation using Direct Line API v3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +title: End a conversation in Bot Framework SDK +description: Learn how to use version 3.0 of the Direct Line API to end conversations between bots and Cortana channels. See how to set up and send endOfConversation events. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - evergreen --- -# End a conversation +# End a conversation in Direct Line API 3.0 -The **endOfConversation** [activity](https://aka.ms/botSpecs-activitySchema) means the channel or bot has ended the conversation. +The **endOfConversation** [activity](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) means the channel or bot has ended the conversation. -> [!NOTE] -> While the **endOfConversation** event is only sent by very few channels, the Cortana channel is the only one that accepts it. Other channels, including Direct Line, do not implement this functionality and instead drop or forward the activity on; each channel determines how to react to an endOfConversation activity. If you are designing a DirectLine client, you would update the client to behave appropriately, such as generating an error if the bot sent an activity to a conversation that has already ended. +> [!NOTE] +> The **endOfConversation** event is sent by very few channels, and few channels accept it. Some channels, including Direct Line, don't implement this functionality and instead drop or forward the activity on; each channel determines how to react to an endOfConversation activity. ## Send an endOfConversation activity -An **endOfConversation** activity ends communication between bot and client. After an **endOfConversation** activity has been sent, the client may still [retrieve messages](bot-framework-rest-direct-line-3-0-receive-activities.md#http-get) using `HTTP GET`, but neither the client nor the bot can send any additional messages to the conversation. - -To end a conversation, simply issue a POST request to send an **endOfConversation** activity. +To request to end a conversation with Cortana channel, POST End of Conversation Activity to the channel's messaging endpoint. ### Request diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md index 0bc0f48d4..a540a58f5 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-receive-activities.md @@ -1,15 +1,17 @@ --- title: Receive activities from the bot - Bot Service -description: Learn how to receive activities from the bot using Direct Line API v3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 06/13/2019 +description: Learn how to receive activity updates from bots. See how to use version 3.0 of the Direct Line API to connect by using WebSocket streams or HTTP GET requests. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Receive activities from the bot +# Receive activities from the bot in Direct Line API 3.0 Using the Direct Line 3.0 protocol, clients can receive activities via `WebSocket` stream or retrieve activities by issuing `HTTP GET` requests. @@ -19,18 +21,20 @@ A streaming WebSocket efficiently pushes messages to clients, whereas the GET in The service allows only 1 WebSocket connection per conversation. Direct Line may close additional WebSocket connections with a reason value of `collision`. -Not all [activity types](https://aka.ms/botSpecs-activitySchema) are available both via WebSocket and via HTTP GET. The following table describes the availability of the various activity types for clients that use the Direct Line protocol. +Not all [activity types](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) are available both via WebSocket and via HTTP GET. The following table describes the availability of the various activity types for clients that use the Direct Line protocol. -| Activity type | Availability | -|----|----| -| message | HTTP GET and WebSocket | -| typing | WebSocket only | -| conversationUpdate | Not sent/received via client | -| contactRelationUpdate | Not supported in Direct Line | -| endOfConversation | HTTP GET and WebSocket | -| all other activity types | HTTP GET and WebSocket | +| Activity type | Availability | +|--------------------------|------------------------------| +| message | HTTP GET and WebSocket | +| typing | WebSocket only | +| conversationUpdate | Not sent/received via client | +| contactRelationUpdate | Not supported in Direct Line | +| endOfConversation | HTTP GET and WebSocket | +| all other activity types | HTTP GET and WebSocket | -## Receive activities via WebSocket stream + + +## Receive activities via WebSocket stream When a client sends a [Start Conversation](bot-framework-rest-direct-line-3-0-start-conversation.md) request to open a conversation with a bot, the service's response includes a `streamUrl` property that the client can subsequently use to connect via WebSocket. The stream URL is preauthorized and therefore the client's request to connect via WebSocket does NOT require an `Authorization` header. @@ -89,15 +93,17 @@ A client should ignore empty messages that it receives from the Direct Line serv A client may send empty messages to the Direct Line service to verify connectivity. The Direct Line service will ignore empty messages that it receives from the client. -The Direct Line service may forcibly close the WebSocket connection under certain conditions. If the client has not received an `endOfConversation` activity, it may [generate a new WebSocket stream URL](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) that it can use to reconnect to the conversation. +The Direct Line service may forcibly close the WebSocket connection under certain conditions. If the client has not received an `endOfConversation` activity, it may [generate a new WebSocket stream URL](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) that it can use to reconnect to the conversation. + +The WebSocket stream contains live updates and very recent messages (since the call to connect via WebSocket was issued) but it doesn't include messages that were sent prior to the most recent `POST` to `/v3/directline/conversations/{id}`. To retrieve messages that were sent earlier in the conversation, use `HTTP GET` as described below. -The WebSocket stream contains live updates and very recent messages (since the call to connect via WebSocket was issued) but it does not include messages that were sent prior to the most recent `POST` to `/v3/directline/conversations/{id}`. To retrieve messages that were sent earlier in the conversation, use `HTTP GET` as described below. + -## Retrieve activities with HTTP GET +## Retrieve activities with HTTP GET Clients that are unable to use WebSockets can retrieve activities by using `HTTP GET`. -To retrieve messages for a specific conversation, issue a `GET` request to the `/v3/directline/conversations/{conversationId}/activities` endpoint, optionally specifying the `watermark` parameter to indicate the most recent message seen by the client. +To retrieve messages for a specific conversation, issue a `GET` request to the `/v3/directline/conversations/{conversationId}/activities` endpoint, optionally specifying the `watermark` parameter to indicate the most recent message seen by the client. The following snippets provide an example of the Get Conversation Activities request and response. The Get Conversation Activities response contains `watermark` as a property of the [ActivitySet](bot-framework-rest-direct-line-3-0-api-reference.md#activityset-object). Clients should page through the available activities by advancing the `watermark` value until no activities are returned. @@ -151,17 +157,17 @@ HTTP/1.1 200 OK Most clients wish to retain a complete message history. Even though Direct Line is a multi-part protocol with potential timing gaps, the protocol and service is designed to make it easy to build a reliable client. -- The `watermark` property that is sent in the WebSocket stream and Get Conversation Activities response is reliable. A client will not miss any messages as long as it replays the watermark verbatim. +- The `watermark` property that is sent in the WebSocket stream and Get Conversation Activities response is reliable. A client won't miss any messages as long as it replays the watermark verbatim. - When a client starts a conversation and connects to the WebSocket stream, any activities that are sent after the POST but before the socket is opened are replayed before new activities. -- When a client issues a Get Conversation Activities request (to refresh history) while it is connected to the WebSocket stream, activities may be duplicated across both channels. Clients should keep track of all known activity IDs so that they are able to reject duplicate activities, should they occur. +- When a client issues a Get Conversation Activities request (to refresh history) while it's connected to the WebSocket stream, activities may be duplicated across both channels. Clients should keep track of all known activity IDs so that they're able to reject duplicate activities, should they occur. Clients that poll using `HTTP GET` should choose a polling interval that matches their intended use. - Service-to-service applications often use a polling interval of 5s or 10s. -- Client-facing applications often use a polling interval of 1s, and issue a single additional request shortly after every message that the client sends (to rapidly retrieve a bot's response). This delay can be as short at 300ms but should be tuned based on the bot's speed and transit time. Polling should not be more frequent than once per second for any extended period of time. +- Client-facing applications often use a polling interval of 1s, and issue a single additional request shortly after every message that the client sends (to rapidly retrieve a bot's response). This delay can be as short at 300ms but should be tuned based on the bot's speed and transit time. Polling shouldn't be more frequent than once per second for any extended period of time. ## Additional resources diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md index c6acf1e48..c7c6f908c 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md @@ -1,28 +1,30 @@ --- -title: Reconnect to a conversation - Bot Service -description: Learn how to reconnect to a conversation using Direct Line API v3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/09/2019 +title: Reconnect to a conversation in Bot Framework SDK +description: Learn how to reconnect to a conversation after losing contact. See how to use Direct Line API version 3.0 to generate new WebSocket stream URLs. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: reference +ms.custom: + - evergreen --- -# Reconnect to a conversation +# Reconnect to a conversation in Direct Line API 3.0 If a client is using the [WebSocket interface](bot-framework-rest-direct-line-3-0-receive-activities.md#connect-via-websocket) to receive messages but loses its connection, it may need to reconnect. In this scenario, the client must generate a new WebSocket stream URL that it can use to reconnect to the conversation. ## Generate a new WebSocket stream URL -To generate a new WebSocket stream URL that can be used to reconnect to an existing conversation, issue this request: +To generate a new WebSocket stream URL that can be used to reconnect to an existing conversation, issue this request: ```http GET https://directline.botframework.com/v3/directline/conversations/{conversationId}?watermark={watermark_value} Authorization: Bearer SECRET_OR_TOKEN ``` -In this request URI, replace **{conversationId}** with the conversation ID and replace **{watermark_value}** with the watermark value (if the `watermark` parameter is supplied). The `watermark` parameter is optional. If the `watermark` parameter is specified in the request URI, the conversation replays from the watermark, guaranteeing that no messages are lost. If the `watermark` parameter is omitted from the request URI, only messages received after the reconnection request are replayed. +In this request URI, replace **{conversationId}** with the conversation ID and replace **{watermark_value}** with the watermark value (if the `watermark` parameter is available). The `watermark` parameter is optional. If the `watermark` parameter is specified in the request URI, the conversation replays from the watermark, guaranteeing that no messages are lost. If the `watermark` parameter is omitted from the request URI, only messages received after the reconnection request are replayed. The following snippets provide an example of the Reconnect request and response. @@ -52,9 +54,9 @@ HTTP/1.1 200 OK ## Reconnect to the conversation -The client must use the new WebSocket stream URL to [reconnect to the conversation](bot-framework-rest-direct-line-3-0-receive-activities.md#connect-via-websocket) within 60 seconds. If the connection cannot be established during this time, the client must issue another Reconnect request to generate a new stream URL. +The client must use the new WebSocket stream URL to [reconnect to the conversation](bot-framework-rest-direct-line-3-0-receive-activities.md#connect-via-websocket) within 60 seconds. If the connection can't be established during this time, the client must issue another Reconnect request to generate a new stream URL. -If you have "Enhanced authentication option" enabled in the Direct Line settings, you might get a 400 "MissingProperty" error saying no user ID specified. +If you have "Enhanced authentication option" enabled in the Direct Line settings, you might get a 400 "MissingProperty" error if you don't have a correctly configured token attached to the request. ## Additional resources diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-send-activity.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-send-activity.md index 560da5a46..f95a40622 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-send-activity.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-send-activity.md @@ -1,17 +1,19 @@ --- title: Send an activity the bot - Bot Service -description: Learn how to send an activity to the bot using Direct Line API v3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to send activity information to bots. See how to use version 3.0 of the Direct Line API to send Activity objects with optional attachments. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Send an activity to the bot +# Send an activity to the bot in Direct Line API 3.0 -Using the Direct Line 3.0 protocol, clients and bots may exchange several different types of [activities](https://aka.ms/botSpecs-activitySchema), including **message** activities, **typing** activities, and custom activities that the bot supports. A client may send a single activity per request. +Using the Direct Line 3.0 protocol, clients and bots may exchange several different types of [activities](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md), including **message** activities, **typing** activities, and custom activities that the bot supports. A client may send a single activity per request. ## Send an activity @@ -30,6 +32,7 @@ Content-Type: application/json ```json { + "locale": "en-EN", "type": "message", "from": { "id": "user1" @@ -43,7 +46,7 @@ Content-Type: application/json When the activity is delivered to the bot, the service responds with an HTTP status code that reflects the bot's status code. If the bot generates an error, an HTTP 502 response ("Bad Gateway") is returned to the client in response to its Send Activity request. > [!NOTE] -> This can be caused by the fact that a correct token was not used. Only the token which was received against *start conversation* can be used to send an activity. +> This can be caused by the fact that a correct token wasn't used. Only the token which was received against *start conversation* can be used to send an activity. If the POST is successful, the response contains a JSON payload that specifies the ID of the Activity that was sent to the bot. @@ -72,17 +75,23 @@ The total time to POST a message to a Direct Line conversation is the sum of the In some situations, a client may need to send attachments to the bot such as images or documents. A client may send attachments to the bot either by [specifying the URL(s)](#send-by-url) of the attachment(s) within the [Activity][] object that it sends using `POST /v3/directline/conversations/{conversationId}/activities` or by [uploading attachment(s)](#upload-attachments) using `POST /v3/directline/conversations/{conversationId}/upload`. -## Send attachment(s) by URL + + +## Send attachment(s) by URL To send one or more attachments as part of the [Activity][] object using `POST /v3/directline/conversations/{conversationId}/activities`, simply include one or more [Attachment][] objects within the Activity object and set the `contentUrl` property of each Attachment object to specify the HTTP, HTTPS, or `data` URI of the attachment. -## Send attachment(s) by upload + + +## Send attachment(s) by upload Often, a client may have image(s) or document(s) on a device that it wants to send to the bot, but no URLs corresponding to those files. In this situation, a client can can issue a `POST /v3/directline/conversations/{conversationId}/upload` request to send attachments to the bot by upload. The format and contents of the request will depend upon whether the client is [sending a single attachment](#upload-one-attachment) or [sending multiple attachments](#upload-multiple-attachments). -### Send a single attachment by upload + -To send a single attachment by upload, issue this request: +### Send a single attachment by upload + +To send a single attachment by upload, issue this request: ```http POST https://directline.botframework.com/v3/directline/conversations/{conversationId}/upload?userId={userId} @@ -125,11 +134,13 @@ HTTP/1.1 200 OK } ``` -### Send multiple attachments by upload + + +### Send multiple attachments by upload -To send multiple attachments by upload, `POST` a multipart request to the `/v3/directline/conversations/{conversationId}/upload` endpoint. Set the `Content-Type` header of the request to `multipart/form-data` and include the `Content-Type` header and `Content-Disposition` header for each part to specify each attachment's type and filename. In the request URI, set the `userId` parameter to the ID of the user that is sending the message. +To send multiple attachments by upload, `POST` a multipart request to the `/v3/directline/conversations/{conversationId}/upload` endpoint. Set the `Content-Type` header of the request to `multipart/form-data` and include the `Content-Type` header and `Content-Disposition` header for each part to specify each attachment's type and filename. In the request URI, set the `userId` parameter to the ID of the user that is sending the message. -You may include an `Activity` object within the request by adding a part that specifies the `Content-Type` header value `application/vnd.microsoft.activity`. If the request includes an Activity, the attachments that are specified by other parts of the payload are added as attachments to that Activity before it is sent. If the request does not include an Activity, an empty Activity is created to serve as the container in which the specified attachments are sent. +You may include an `Activity` object within the request by adding a part that specifies the `Content-Type` header value `application/vnd.microsoft.activity`. If the request includes an Activity, the attachments that are specified by other parts of the payload are added as attachments to that Activity before it's sent. If the request doesn't include an Activity, an empty Activity is created to serve as the container in which the specified attachments are sent. The following snippets provide an example of the Send (multiple) Attachments request and response. In this example, the request sends a message that contains some text and a single image attachment. Additional parts could be added to the request to include multiple attachments in this message. @@ -186,7 +197,7 @@ HTTP/1.1 200 OK - [Reconnect to a conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) - [Receive activities from the bot](bot-framework-rest-direct-line-3-0-receive-activities.md) - [End a conversation](bot-framework-rest-direct-line-3-0-end-conversation.md) -- [Bot Framework Activity schema](https://aka.ms/botSpecs-activitySchema) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) [Activity]: bot-framework-rest-connector-api-reference.md#activity-object [Attachment]: bot-framework-rest-connector-api-reference.md#attachment-object diff --git a/articles/rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md b/articles/rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md index 1d4e1ca64..9fe10f2f5 100644 --- a/articles/rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md +++ b/articles/rest-api/bot-framework-rest-direct-line-3-0-start-conversation.md @@ -1,28 +1,30 @@ --- title: Start a conversation - Bot Service -description: Learn how to start a conversation using Direct Line API v3.0. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +description: Learn how to use version 3.0 of the Direct Line API to start conversations with bots. Find out how the start conversation and generate token operations differ. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Start a conversation +# Start a conversation in Direct Line API 3.0 Direct Line conversations are explicitly opened by clients and may run as long as the bot and client participate and have valid credentials. While the conversation is open, both the bot and client may send messages. More than one client may connect to a given conversation and each client may participate on behalf of multiple users. ## Open a new conversation -To open a new conversation with a bot, issue this request: +To open a new conversation from your client, issue POST to the /v3/directline/conversations endpoint. ```http POST https://directline.botframework.com/v3/directline/conversations Authorization: Bearer SECRET_OR_TOKEN ``` -The following snippets provide an example of the Start Conversation request and response. +The following snippets provide an example of the start conversation request and response. ### Request @@ -49,20 +51,20 @@ HTTP/1.1 201 Created } ``` -Typically, a Start Conversation request is used to open a new conversation and an **HTTP 201** status code is returned if the new conversation is successfully started. However, if a client submits a Start Conversation request with a Direct Line token in the `Authorization` header that has previously been used to start a conversation using the Start Conversation operation, an **HTTP 200** status code will be returned to indicate that the request was acceptable but no conversation was created (as it already existed). +Typically, a start conversation request is used to open a new conversation and an **HTTP 201** status code is returned if the new conversation is successfully started. However, if a client submits a start conversation request with a Direct Line token in the `Authorization` header that has previously been used to start a conversation using the start conversation operation, an **HTTP 200** status code will be returned to indicate that the request was acceptable but no conversation was created (as it already existed). > [!TIP] -> You have 60 seconds to connect to the WebSocket stream URL. If the connection cannot be established during this time, you can [reconnect to the conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) to generate a new stream URL. +> You have 60 seconds to connect to the WebSocket stream URL. If the connection can't be established during this time, you can [reconnect to the conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) to generate a new stream URL. -## Start Conversation versus Generate Token +## Start conversation versus generate token -The Start Conversation operation (`POST /v3/directline/conversations`) is similar to the [Generate Token](bot-framework-rest-direct-line-3-0-authentication.md#generate-token) operation (`POST /v3/directline/tokens/generate`) in that both operations return a `token` that can be used to access a single conversation. However, the Start Conversation operation also starts the conversation, contacts the bot, and creates a WebSocket stream URL, whereas the Generate Token operation does none of these things. +The start conversation operation (`POST /v3/directline/conversations`) is similar to the [generate token](bot-framework-rest-direct-line-3-0-authentication.md#generate-token) operation (`POST /v3/directline/tokens/generate`) in that both operations return a `token` that can be used to access a single conversation. However, the start conversation operation also starts the conversation, contacts the bot, and creates a WebSocket stream URL, whereas the generate token operation does none of these things. -If you intend to start the conversation immediately, use the Start Conversation operation. If you plan to distribute the token to clients and want them to initiate the conversation, use the [Generate Token](bot-framework-rest-direct-line-3-0-authentication.md#generate-token) operation instead. +If you intend to start the conversation immediately with your client, use the start conversation operation. If you plan to distribute the token to clients and want them to initiate the conversation, use the [generate token](bot-framework-rest-direct-line-3-0-authentication.md#generate-token) operation instead. ## Additional resources - [Key concepts](bot-framework-rest-direct-line-3-0-concepts.md) - [Authentication](bot-framework-rest-direct-line-3-0-authentication.md) - [Receive activities via WebSocket stream](bot-framework-rest-direct-line-3-0-receive-activities.md#connect-via-websocket) -- [Reconnect to a conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) \ No newline at end of file +- [Reconnect to a conversation](bot-framework-rest-direct-line-3-0-reconnect-to-conversation.md) diff --git a/articles/rest-api/bot-framework-rest-overview.md b/articles/rest-api/bot-framework-rest-overview.md index 90b53e0b4..6ab9e4821 100644 --- a/articles/rest-api/bot-framework-rest-overview.md +++ b/articles/rest-api/bot-framework-rest-overview.md @@ -1,33 +1,33 @@ --- title: Bot Framework REST APIs - Bot Service description: Get started with the Bot Framework REST APIs that can be used to build bots and clients that connect to bots. -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: overview +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Bot Framework REST APIs -> [!div class="op_single_selector"] -> - [.NET](../dotnet/bot-builder-dotnet-overview.md) -> - [Node.js](../nodejs/bot-builder-nodejs-overview.md) -> - [REST](../rest-api/bot-framework-rest-overview.md) -The Bot Framework REST APIs enable you to build bots that exchange messages with channels configured in the [Azure Portal](https://portal.azure.com), store and retrieve state data, and connect your own client applications to your bots. All Bot Framework services use industry-standard REST and JSON over HTTPS. +Most Bot Framework bots are built using the Bot Framework SDK, which organizes your bot and handles all conversations for you. An alternative to using the SDK is to send messages directly to the bot using a REST API. ## Build a bot -You can create a bot with any programming language by using the Bot Connector service to exchange messages with channels configured in the Bot Framework Portal. +By coding with Bot Framework REST APIs, you can send and receive messages with users on any channel configured in your bot's Azure AI Bot Service registration. > [!TIP] -> The Bot Framework provides client libraries that can be used to build bots in either C# or Node.js. -> To build a bot using C#, use the [Bot Framework SDK for C#](../dotnet/bot-builder-dotnet-overview.md). -> To build a bot using Node.js, use the [Bot Framework SDK for Node.js](../nodejs/index.md). +> The Bot Framework provides client libraries that can be used to build bots in either C# or Node.js. +> To build a bot using C#, use the [Bot Framework SDK for C#](../dotnet/bot-builder-dotnet-overview.md). +> To build a bot using Node.js, use the [Bot Framework SDK for Node.js](../nodejs/index.md). -To learn more about building bots using the Bot Connector service, see [Key concepts](bot-framework-rest-connector-concepts.md). +Refer to the [Azure AI Bot Service](../bot-service-overview-introduction.md) docs to learn more about building bots using the service. -## Build a client +## Build a Direct Line client -You can enable your own client application to communicate with your bot by using the Direct Line API. The Direct Line API implements an authentication mechanism that uses standard secret/token patterns and provides a stable schema, even if your bot changes its protocol version. To learn more about using the Direct Line API to enable communication between a client and your bot, see [Key concepts](bot-framework-rest-direct-line-3-0-concepts.md). \ No newline at end of file +Most channels such as Facebook, Teams, or Slack provide clients, but with Direct Line you can enable your own client application to communicate with your bot. [Web Chat](https://github.com/microsoft/BotFramework-WebChat) is an open source example of a Direct Line client, and it can be used as-is or modified or learned from when making your own client. The Direct Line API implements an authentication mechanism that uses standard secret/token patterns and provides a stable schema, even if your bot changes its protocol version. To learn more about using the Direct Line API to enable communication between a client and your bot, see [Key concepts](bot-framework-rest-direct-line-3-0-concepts.md). + +Direct Line clients can be in different languages and locations (for example, a desktop app instead of a web page). For more information, see [About Direct Line](../bot-service-channel-directline.md). diff --git a/articles/rest-api/bot-framework-rest-state.md b/articles/rest-api/bot-framework-rest-state.md index 3295aa146..8d5b97cb6 100644 --- a/articles/rest-api/bot-framework-rest-state.md +++ b/articles/rest-api/bot-framework-rest-state.md @@ -1,14 +1,20 @@ --- title: Manage state data - Bot Service -description: Learn how to store and retrieve state data using the Bot State service. -author: RobStand -ms.author: kamrani -manager: kamrani +description: Learn about alternatives to the deprecated Bot State service. See how to keep track of information about users and conversations without using this service. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow ms.topic: article -ms.service: bot-service -ms.date: 12/13/2017 +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Manage state data +# Manage state data with the REST API -The Microsoft Bot Framework State service is retired as of March 30, 2018. Previously, bots built on the Azure Bot Service or the Bot Builder SDK had a default connection to this service hosted by Microsoft to store bot state data. Bots will need to be updated to use their own state storage. +Bots typically use storage to keep track of a user's place in the conversation or details relevant to their relationship with the bot. The Bot Framework SDK manages user and conversation state automatically for bot developers. + +Originally, Bot Framework shipped with a state service for storing this data, but most modern bots (and all recent releases of the Bot Framework SDK) use storage controlled directly by the bot developer instead of this cMicrosoft Entrally managed service. + +The cMicrosoft Entral Bot Framework State service is retired as of March 30, 2018. For more information, see [Reminder: The Bot Framework State service has been retired—what you need to know](https://blog.botframework.com/2018/04/02/reminder-the-bot-framework-state-service-has-been-retired-what-you-need-to-know/). diff --git a/articles/v4sdk/abs-quickstart.md b/articles/v4sdk/abs-quickstart.md index 944c3c384..c7c0dee1d 100644 --- a/articles/v4sdk/abs-quickstart.md +++ b/articles/v4sdk/abs-quickstart.md @@ -1,84 +1,60 @@ --- -title: Create a bot with Azure Bot Service - Bot Service -description: Learn how to create a bot with Bot Service, an integrated, dedicated bot development environment. -keywords: Quickstart, create bot, bot service, web app bot -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/09/2020 +title: Create an Azure Bot resource in the Azure portal +description: Learn how to use the Azure portal to create a bot resource for the Azure AI Bot Service, an integrated, dedicated bot development environment. +keywords: Quickstart, create bot resource, bot service, Azure Bot +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: quickstart +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Create a bot with Azure Bot Service +# Use the Azure portal to Create an Azure Bot resource -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Azure Bot Service provides the core components for creating bots, including the Bot Framework SDK for developing bots and the bot service for connecting bots to channels. In the topic, you'll be able to choose either .NET or Node.js template to create a bot using the Bot Framework SDK v4. +The _Azure Bot_ resource (_bot resource_) allows you to register your bot with Azure AI Bot Service and to connect your bot to channels. You can build, connect, and manage bots to interact with your users wherever they are, from your app or website to Teams, Messenger and many other channels. ->[!NOTE] -> The bot you create is automatically registered with the Azure Bot Service. If you already have a bot hosted elsewhere and you want to register it, see the article: [Register a bot with Azure Bot Service](../bot-service-quickstart-registration.md). +This article describes how to create a bot resource through the Azure portal. -[!INCLUDE [Azure vs local development](~/includes/snippet-quickstart-paths.md)] +- To learn how to create a bot, see the [Create a bot with the Bot Framework SDK](../bot-service-quickstart-create-bot.md) quickstart. +- For information on how to provision and publish a bot to Azure, see how to [Deploy your bot in Azure](../bot-builder-deploy-az-cli.md). -## Prerequisites - -- [Azure](https://portal.azure.com) account - -### Create a new bot service - -1. Log in to the [Azure portal](https://portal.azure.com/). -1. Click **Create new resource** link found on the upper left-hand corner of the Azure portal, then select **AI + Machine Learning** > **Web App bot**. - -![create bot](../media/azure-bot-quickstarts/abs-create-blade.png) - -2. A *new blade* will open with information about the **Web App Bot**. +## Managing resources -3. In the **Bot Service** blade, provide the requested information about your bot as specified in the table below the image.
- ![Create Web App Bot blade](../media/azure-bot-quickstarts/sdk-create-bot-service-blade.png) +When you create a bot resource, Azure creates associated resources. +Some of the resources created depend on how you decide to manage your bot's identity. - | Setting | Suggested value | Description | - | ---- | ---- | ---- | - | **Bot name** | Your bot's display name | The display name for the bot that appears in channels and directories. This name can be changed at anytime. | - | **Subscription** | Your subscription | Select the Azure subscription you want to use. | - | **Resource Group** | myResourceGroup | You can create a new [resource group](/azure/azure-resource-manager/resource-group-overview#resource-groups) or choose from an existing one. | - | **Location** | The default location | Select the geographic location for your resource group. Your location choice can be any location listed, though it's often best to choose a location closest to your customer. The location cannot be changed once the bot is created. | - | **Pricing tier** | F0 | Select a pricing tier. You may update the pricing tier at any time. For more information, see [Bot Service pricing](https://azure.microsoft.com/pricing/details/bot-service/). | - | **App name** | A unique name | The unique URL name of the bot. For example, if you name your bot *myawesomebot*, then your bot's URL will be `http://myawesomebot.azurewebsites.net`. The name must use alphanumeric and underscore characters only. There is a 35 character limit to this field. The App name cannot be changed once the bot is created. | - | **Bot template** | Echo bot | Choose **SDK v4**. Select either C# or Node.js for this quickstart, then click **Select**. - | **App service plan/Location** | Your app service plan | Select an [app service plan](https://azure.microsoft.com/pricing/details/app-service/plans/) location. Your location choice can be any location listed, though it's often best to choose the same location as the bot service. | - | **LUIS Accounts** _Only available for Basic Bot template_ | LUIS Azure Resource Name | After [migrating LUIS Resources over to an Azure Resource](https://docs.microsoft.com/azure/cognitive-services/luis/luis-migration-authoring), input the Azure Resource name to associate this LUIS Application with that Azure Resource. - | **Application Insights** | On | Decide if you want to turn [Application Insights](/bot-framework/bot-service-manage-analytics) **On** or **Off**. If you select **On**, you must also specify a regional location. Your location choice can be any location listed, though it's often best to choose the same location as the bot service. | - | **Microsoft App ID and password** | Auto create App ID and password | Use this option if you need to manually enter a Microsoft App ID and password. Otherwise, a new Microsoft App ID and password will be created for you in the bot creation process. When creating an app registration manually for the Bot Service, please ensure that the supported account types is set to ‘Accounts in any organizational directory’ or ‘Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Outlook.com, Xbox, etc.)’ | +[!INCLUDE [identity-app-type-support](../includes/azure-bot-resource/identity-app-type-support.md)] -4. Click **Create** to create the service and deploy the bot to the cloud. This process may take several minutes. - -Confirm that the bot has been deployed by checking the **Notifications**. The notifications will change from **Deployment in progress...** to **Deployment succeeded**. Click **Go to resource** button to open the bot's resources blade. - -Now that your bot is created, test it in Web Chat. - -## Test the bot -In the **Bot Management** section, click **Test in Web Chat**. Azure Bot Service will load the Web Chat control and connect to your bot. +## Prerequisites -![Azure Webchat test](../media/azure-bot-quickstarts/azure-webchat-test.png) +- If you don't have an Azure subscription, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. +- The Bot Framework SDK for C# or JavaScript version 4.15.0 or later, for user-assigned managed identity and single-tenant bots. -Enter a message and your bot should respond. +[!INCLUDE [azure bot resource](../includes/azure-bot-resource/azure-bot-resource.md)] -## Manual app registration +## Additional information -A manual registration is necessary for situations like: +- For information about identity management with Microsoft Entra ID, see [What is Microsoft Entra ID?](/azure/active-directory/fundamentals/active-directory-whatis). +- For information about Azure App Service and App Service plans, see the [App Service overview](/azure/app-service/overview). +- For information about Azure resources and how they're managed in general, see the [Azure Resource Manager overview](/azure/azure-resource-manager/management/overview). -- You are unable to make the registrations in your organization and need another party to create the App ID for the bot you're building. -- You need to manually create your own app ID (and password). +> [!NOTE] +> The Bot Framework Composer and Bot Framework Emulator currently only support multi-tenant bots. +> The Bot Framework SDK for C# or JavaScript version 4.15.0 or later is required for user-assigned managed identity and single-tenant bots. -See [FAQ - App Registration](../bot-service-resources-bot-framework-faq.md#app-registration). +### Skill support +[!INCLUDE [skills-and-identity-types](../includes/skills-and-identity-types.md)] -## Download code -You can download the code to work on it locally. -1. In the **Bot Management** section, click **Build**. -1. Click on **Download Bot source code** link in the right-pane. -1. Follow the prompts to download the code, and then unzip the folder. - 1. [!INCLUDE [download keys snippet](../includes/snippet-abs-key-download.md)] +For information on how to configure a skill or skill consumer, see [Implement a skill](skill-implement-skill.md) or [Implement a skill consumer](skill-implement-consumer.md). ## Next steps -After you download the code, you can continue to develop the bot locally on your machine. Once you test your bot and are ready to upload the bot code to the Azure portal, follow the instructions listed under [set up continous deployment](../bot-service-build-continuous-deployment.md) topic to automatically update code after you make changes. + +- [Create a bot with Bot Framework Composer](/composer/quickstart-create-bot) +- [Create a bot with the Bot Framework SDK](../bot-service-quickstart-create-bot.md) diff --git a/articles/v4sdk/bf-cli-overview.md b/articles/v4sdk/bf-cli-overview.md index c5b489746..92e4c16c2 100644 --- a/articles/v4sdk/bf-cli-overview.md +++ b/articles/v4sdk/bf-cli-overview.md @@ -1,89 +1,24 @@ --- -title: Azure Bot Framework Command-Line Interface (CLI) overview - Bot Service -description: Learn about the Bot Framework Command-Line Interface (CLI). -keywords: Bot Framework Command-Line Interface, Bot Framework CLI +title: Azure Bot Framework CLI tool - Bot Service +description: About the Bot Framework Command-Line Interface (CLI) tool. author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/01/2019 +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: reference +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- - -# Bot Framework CLI overview +# Bot Framework CLI tool -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Bot Framework Command-Line Interface (CLI) is a cross-platform tool that allows you to manage bots and related services. It replaces a collection of older standalone CLI tools by aggregating them into a single tool. +You can use the Bot Framework CLI to manage Bot Framework bots and related services. The CLI aggregates a collection of cross-platform tools into one cohesive and consistent interface. -## Prerequisites +## Additional information -* [Node.js](https://nodejs.org/), version 10.14.1 or later. - -## Installation - -Install the BF CLI from the command line. - -~~~cmd -npm i -g @microsoft/botframework-cli -~~~ - -## Available commands - -The following commands are currently available. - -| Old tool | BF command set | Description | -| :--- | :--- | :--- | -| ChatDown | [`bf chatdown`](bf-cli-reference.md#bf-chatdown) | Commands for working with chat dialog (**.chat**) files. | -| na | [`bf config`](bf-cli-reference.md#bf-config) | Configures various settings within the CLI. | -| LuDown, LuisGen | [`bf luis`](bf-cli-reference.md#bf-luis) | Commands for working with LUIS resource files and managing LUIS models. | -| QnAMaker | [`bf qnamaker`](bf-cli-reference.md#bf-qnamaker) | Commands for working with QnA Maker resource files and managing knowledge bases. | - -The following tools will be ported in upcoming releases: -- LUIS (API) -- Dispatch - -See [Porting Map](https://github.com/microsoft/botframework-cli/blob/master/PortingMap.md) for a mapping reference between the old and new tools. - -_Note: The older CLI tools will be deprecated in upcoming releases and support for them will end in the future. -All new investments, bug fixes, and new features in this area will target only the BF CLI._ - -## Overview - -The BF CLI manages bots and related services. It is part of the Microsoft Bot Framework, a comprehensive framework for building enterprise-grade conversational AI experiences. In addition to managing bot related resources, the BF CLI can be used as part of continuous integration and continuous deployment (CI/CD) pipelines. As you build your bot, you might also need to integrate AI services like LUIS for language understanding, QnAMaker for your bot to respond to simple questions in a Q&A format, and more. To integrate AI service in your bot, use: - -* [`bf luis`](bf-cli-reference.md#bf-luis) command to work with LUIS **.lu** resource files and manage LUIS models. It can also generate corresponding source (C# or JavaScript) code. -* [LUIS APIs tool](https://github.com/microsoft/botbuilder-tools/tree/master/packages/LUIS/readme.md) to deploy the local files, train, test, and publish them as Language Understanding models within the LUIS service. -* [`bf qnamaker`](bf-cli-reference.md#bf-qnamaker) command to work with QnAMaker knowledge bases. It can create and manage QnA Maker assets both locally, and on the QnA Maker service. - -* Refer to the lu library [documentation](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/README.md) for how to work with **.lu** and **.qna** file formats. - -As your bot becomes more sophisticated, use the [Dispatch](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/Dispatch) CLI tool to create, evaluate, and dispatch intent across multiple LUIS models and QnA Maker knowledge bases. - -To test and refine your bot, you can use the new [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/releases). The Emulator enables you to test and debug your bots on local machine or in the cloud. - -During early design stages you might want to create mock conversations between the user and the bot for the specific scenarios your bot will support. Use [`bf chatdown`](bf-cli-reference.md#bf-chatdown) command to author conversation mockup **.chat** files, convert them into rich transcripts, and view the conversations in the the Emulator. - -Lastly, with the [Azure CLI](https://github.com/microsoft/botframework-cli/blob/master/AzureCli.md) (`az bot` command), you can create, download, publish, and configure channels with the [Azure Bot Service](https://azure.microsoft.com/services/bot-service/). It is a plugin that extends the functionality of the [Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) to manage your Azure Bot Service assets. - -## Privacy and instrumentation -BF CLI contains instrumentation options that are designed to help us improve the tool based on **anonymous** usage patterns. __It is disabled, opted-out by default__. If you elect to opt-in, Microsoft gathers some usage data: - -* Command group calls -* Flags used, **excluding** specific values. For example, for the parameter `--folder:name`, only the use of `--folder` is gathered, the folder name is not gathered. - -To modify data collection behavior, use the [`bf config`](bf-cli-reference.md#bf-config) command. - -Please refer to [Microsoft Privacy Statement](https://privacy.microsoft.com/privacystatement) for more details. - -## Issues and feature requests -- You can file issues and feature requests [here](https://github.com/microsoft/botframework-cli/issues). -- You can find known issues [here](https://github.com/microsoft/botframework-cli/labels/known-issues). - -## Next steps -- [BF cli reference](bf-cli-reference.md) +- The [Bot Framework Tools repository](https://github.com/microsoft/botframework-cli#readme) +- [Bot Framework CLI command reference](https://github.com/microsoft/botframework-cli/tree/master/packages/cli#readme) diff --git a/articles/v4sdk/bf-cli-reference.md b/articles/v4sdk/bf-cli-reference.md deleted file mode 100644 index cc63ad5f5..000000000 --- a/articles/v4sdk/bf-cli-reference.md +++ /dev/null @@ -1,852 +0,0 @@ ---- -title: Azure Bot Framework Command-Line Interface (CLI) reference - Bot Service -description: Learn about the Bot Framework Command-Line Interface (CLI). -keywords: Bot Framework Command-Line Interface, Bot Framework CLI -author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 10/25/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Bot Framework CLI reference - -[!INCLUDE[applies-to](../includes/applies-to.md)] - -Usage - -```sh-session -npm install -g @microsoft/botframework-cli -``` - -## Commands - -* [`bf chatdown`](#bf-chatdown) -* [`bf chatdown:convert`](#bf-chatdownconvert) -* [`bf config`](#bf-config) -* [`bf config:set:qnamaker`](#bf-configsetqnamaker) -* [`bf config:set:telemetry`](#bf-configsettelemetry) -* [`bf config:show`](#bf-configshow) -* [`bf config:show:qnamaker`](#bf-configshowqnamaker) -* [`bf config:show:telemetry`](#bf-configshowtelemetry) -* [`bf help [COMMAND]`](#bf-help-command) -* [`bf luis`](#bf-luis) -* [`bf luis:convert`](#bf-luisconvert) -* [`bf luis:generate:cs`](#bf-luisgeneratecs) -* [`bf luis:generate:ts`](#bf-luisgeneratets) -* [`bf luis:translate`](#bf-luistranslate) -* [`bf qnamaker`](#bf-qnamaker) -* [`bf qnamaker:alterations`](#bf-qnamakeralterations) -* [`bf qnamaker:alterations:list`](#bf-qnamakeralterationslist) -* [`bf qnamaker:alterations:replace`](#bf-qnamakeralterationsreplace) -* [`bf qnamaker:convert`](#bf-qnamakerconvert) -* [`bf qnamaker:endpointkeys`](#bf-qnamakerendpointkeys) -* [`bf qnamaker:endpointkeys:list`](#bf-qnamakerendpointkeyslist) -* [`bf qnamaker:endpointkeys:refresh`](#bf-qnamakerendpointkeysrefresh) -* [`bf qnamaker:endpointsettings`](#bf-qnamakerendpointsettings) -* [`bf qnamaker:endpointsettings:get`](#bf-qnamakerendpointsettingsget) -* [`bf qnamaker:endpointsettings:update`](#bf-qnamakerendpointsettingsupdate) -* [`bf qnamaker:init`](#bf-qnamakerinit) -* [`bf qnamaker:kb`](#bf-qnamakerkb) -* [`bf qnamaker:kb:create`](#bf-qnamakerkbcreate) -* [`bf qnamaker:kb:delete`](#bf-qnamakerkbdelete) -* [`bf qnamaker:kb:export`](#bf-qnamakerkbexport) -* [`bf qnamaker:kb:get`](#bf-qnamakerkbget) -* [`bf qnamaker:kb:list`](#bf-qnamakerkblist) -* [`bf qnamaker:kb:publish`](#bf-qnamakerkbpublish) -* [`bf qnamaker:kb:replace`](#bf-qnamakerkbreplace) -* [`bf qnamaker:kb:update`](#bf-qnamakerkbupdate) -* [`bf qnamaker:operationdetails`](#bf-qnamakeroperationdetails) -* [`bf qnamaker:operationdetails:get`](#bf-qnamakeroperationdetailsget) -* [`bf qnamaker:query`](#bf-qnamakerquery) -* [`bf qnamaker:train`](#bf-qnamakertrain) -* [`bf qnamaker:translate`](#bf-qnamakertranslate) - -## `bf chatdown` - -Converts chat dialog files in \.chat format into transcript files. Writes corresponding \.transcript for each .chat file. - -```text -USAGE - $ bf chatdown - -OPTIONS - -h, --help Chatdown command help -``` - -_See code: [@microsoft/bf-chatdown](https://github.com/microsoft/botframework-cli/tree/master/packages/chatdown/src/commands/chatdown/index.ts)_ - -## `bf chatdown:convert` - -Converts chat dialog files in \.chat format into transcript files. Writes corresponding \.transcript for each .chat file. - -```text -USAGE - $ bf chatdown:convert - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help Chatdown command help - - -i, --in=in The path of the chat file or directory to be parsed. A glob expression may be passed containing chat - files to be processed all at once, ex. ./**/*.chat. If flag is omitted, stdin will be used. If an - output directory is not present (-o), it will default the output to the current working directory. - - -o, --out=out Path to the directory where the output of the multiple chat file processing (-o) will be placed. - - -p, --prefix Prefix stdout with package name. - - -s, --stamp Use static timestamps when generating timestamps on activities. - -EXAMPLE - - $ bf chatdown - $ bf chatdown --in=./path/to/file/sample.chat - $ bf chatdown --in ./test/utils/*.sample.chat -o ./ - $ (echo user=Joe && [ConversationUpdate=MembersAdded=Joe]) | bf chatdown --static -``` - -_See code: [@microsoft/bf-chatdown](https://github.com/microsoft/botframework-cli/tree/master/packages/chatdown/src/commands/chatdown/convert.ts)_ - -## `bf config` - -Configures various settings within the CLI. - -```text -USAGE - $ bf config - -OPTIONS - -h, --help config help -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/index.ts)_ - -## `bf config:set:qnamaker` - -Sets the QnA Maker config data - -```text -USAGE - $ bf config:set:qnamaker - -OPTIONS - -h, --help config:set:qnamaker help - --endpointKey=endpointKey QnAMaker endpointKey to be set - --hostname=hostname QnAMaker hostname to be set - --kbId=kbId QnAMaker kbId to be set - --subscriptionKey=subscriptionKey QnAMaker subscriptionkey to be set - -EXAMPLE - - { - "qnamaker_kbId": "3bda64af-dddd-dddd-dddd-021906b093b1", - "qnamaker_subscriptionKey": "nnnnnnnnnnnnnnnnnnnnnnnnn", - "qnamaker_endpointKey": "6b5ecf9c-kkkk-kkkk-kkkk-761489817e5f", - "qnamaker_hostname": "https://{qnaservice-hostname}.azurewebsites.net" - } -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/set/qnamaker.ts)_ - -## `bf config:set:telemetry` - -Enables or disables anonymous data collection to improve the products. (Command group calls and flags usage) - -```text -USAGE - $ bf config:set:telemetry - -OPTIONS - -d, --disable Disable tlemetry - -e, --enable Enable tlemetry - -h, --help config:set:telemetry help -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/set/telemetry.ts)_ - -## `bf config:show` - -Displays the config file - -```text -USAGE - $ bf config:show - -OPTIONS - -h, --help config:show help -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/show.ts)_ - -## `bf config:show:qnamaker` - -Displays QnA Maker settings - -```text -USAGE - $ bf config:show:qnamaker - -OPTIONS - -h, --help config:show:qnamaker help -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/show/qnamaker.ts)_ - -## `bf config:show:telemetry` - -Displays telemetry settings - -```text -USAGE - $ bf config:show:telemetry - -OPTIONS - -h, --help config:show:telemetry help -``` - -_See code: [@microsoft/bf-cli-config](https://github.com/microsoft/botframework-cli/tree/master/packages/config/src/commands/config/show/telemetry.ts)_ - -## `bf help [COMMAND]` - -Displays help for the bf command - -```text -USAGE - $ bf help [COMMAND] - -ARGUMENTS - COMMAND command to show help for - -OPTIONS - --all see all commands in CLI -``` - -_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.1.6/src/commands/help.ts)_ - -## `bf luis` - -Converts, translates luis/lu files or generates source code. - -```text -USAGE - $ bf luis - -OPTIONS - -h, --help Display Luis available commands -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/luis/index.ts)_ - -## `bf luis:convert` - -Converts .lu file(s) to a LUIS application JSON model or vice versa - -```text -USAGE - $ bf luis:convert - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help luis:convert help - -i, --in=in Source .lu file(s) or LUIS application JSON model - -o, --out=out Output file or folder name. If not specified stdout will be used as output - -r, --recurse Indicates if sub-folders need to be considered to file .lu file(s) - --culture=culture Lang code for the LUIS application - --description=description Text describing the LUIS application - --log Enables log messages - --name=name Name of the LUIS application - --schemaversion=schemaversion Schema version of the LUIS application - --sort When set, intent, utterances, entities are alphabetically sorted in .lu files - --versionid=versionid Version ID of the LUIS application -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/luis/convert.ts)_ - -## `bf luis:generate:cs` - -Generates a strongly typed C# source code from an exported (JSON) LUIS model. - -```text -USAGE - $ bf luis:generate:cs - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help luis:generate:cs help - -i, --in=in Path to the file containing the LUIS application JSON model - -o, --out=out Output file or folder name. If not specified stdout will be used as output - --className=className Name of the autogenerated class (can include namespace) -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/luis/generate/cs.ts)_ - -## `bf luis:generate:ts` - -Generates a strongly typed typescript source code from an exported (JSON) LUIS model. - -```text -USAGE - $ bf luis:generate:ts - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help luis:generate:ts help - -i, --in=in Path to the file containing the LUIS application JSON model - -o, --out=out Output file or folder name. If not specified stdout will be used as output - --className=className Name of the autogenerated class -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/luis/generate/ts.ts)_ - -## `bf luis:translate` - -Translates given LUIS application JSON model or .lu file(s) - -```text -USAGE - $ bf luis:translate - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help luis:translate help - -i, --in=in Source .lu file(s) or LUIS application JSON model - -o, --out=out Output folder name. If not specified stdout will be used as output - -r, --recurse Indicates if sub-folders need to be considered to file .lu file(s) - --srclang=srclang Source lang code. Auto detect if missing. - --tgtlang=tgtlang (required) Comma separated list of target languages. - --translate_comments When set, machine translate comments found in .lu file - --translate_link_text When set, machine translate link description in .lu file - --translatekey=translatekey (required) Machine translation endpoint key. -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/luis/translate.ts)_ - -## `bf qnamaker` - -QnA Maker - -```text -USAGE - $ bf qnamaker - -OPTIONS - -h, --help Display QnA Maker CLI available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/index.ts)_ - -## `bf qnamaker:alterations` - -Commands for replacing and listing your alterations - -```text -USAGE - $ bf qnamaker:alterations - -OPTIONS - -h, --help display qnamaker:alterations available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/alterations/index.ts)_ - -## `bf qnamaker:alterations:list` - -Downloads all word alterations (synonyms) that have been added by the user. - -```text -USAGE - $ bf qnamaker:alterations:list - -OPTIONS - -h, --help qnamaker:alterations:list command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/alterations/list.ts)_ - -## `bf qnamaker:alterations:replace` - -Replaces word alterations (synonyms) for the KB with the given records. - -```text -USAGE - $ bf qnamaker:alterations:replace - -OPTIONS - -h, --help qnamaker:alterations:replace command help - -i, --in=in File path to the WordAlterationsDTO object to send in the body of the request - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/alterations/replace.ts)_ - -## `bf qnamaker:convert` - -Converts .lu file(s) to QnA application JSON models or vice versa. - -```text -USAGE - $ bf qnamaker:convert - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help qnamaker:convert help - -i, --in=in Source .qna file(s) or QnA KB JSON file - -o, --out=out Output file or folder name. If not specified stdout will be used as output - -r, --recurse Indicates if sub-folders need to be considered to file .qna file(s) - --alterations Indicates if files is QnA Alterations - --log Enables log messages - --name=name Name of the QnA KB - --sort When set, questions collections are alphabetically sorted are alphabetically sorted in .qna files -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/qnamaker/convert.ts)_ - -## `bf qnamaker:endpointkeys` - -Commands to refresh and list keys - -```text -USAGE - $ bf qnamaker:endpointkeys - -OPTIONS - -h, --help display qnamaker:endpointkeys available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointkeys/index.ts)_ - -## `bf qnamaker:endpointkeys:list` - -Lists all the currently valid endpointKeys for querying your private endpoint - -```text -USAGE - $ bf qnamaker:endpointkeys:list - -OPTIONS - -h, --help qnamaker:endpointkeys:list command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointkeys/list.ts)_ - -## `bf qnamaker:endpointkeys:refresh` - -Re-generates an endpoint key, in case you suspect your keys have been compromised - -```text -USAGE - $ bf qnamaker:endpointkeys:refresh - -OPTIONS - -h, --help qnamaker:endpoints:refresh command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --keyType=keyType (required) Type of Key. (PrimaryKey/SecondaryKey) - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointkeys/refresh.ts)_ - -## `bf qnamaker:endpointsettings` - -Commands to get and update endpoint settings - -```text -USAGE - $ bf qnamaker:endpointsettings - -OPTIONS - -h, --help display qnamaker:update available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointsettings/index.ts)_ - -## `bf qnamaker:endpointsettings:get` - -Gets endpoint settings for an endpoint. - -```text -USAGE - $ bf qnamaker:endpointsettings:get - -OPTIONS - -h, --help qnamaker:endpointsettings:get command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --kbId=kbId Knowledgebase id to get metadata. - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointsettings/get.ts)_ - -## `bf qnamaker:endpointsettings:update` - -Updates endpoint settings for an endpoint. - -```text -USAGE - $ bf qnamaker:endpointsettings:update - -OPTIONS - -h, --help qnamaker:endpointsettings:update command help - --activelearning Enable active learning. Disables if flag not set - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/endpointsettings/update.ts)_ - -## `bf qnamaker:init` - -Initializes the config file with settings. - -```text -USAGE - $ bf qnamaker:init - -OPTIONS - -h, --help qnamaker:init command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/init.ts)_ - -## `bf qnamaker:kb` - -Commands for manipulating your knowledge base - -```text -USAGE - $ bf qnamaker:kb - -OPTIONS - -h, --help display qnamaker:kb available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/index.ts)_ - -## `bf qnamaker:kb:create` - -Creates a new knowledgebase - -```text -USAGE - $ bf qnamaker:kb:create - -OPTIONS - -h, --help qnamaker:kb:create command help - -i, --in=in File path to the CreateKbDTO object to send in the body of the request. - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --name=name Name of the kb you want to create. This will override the name of KB that might be - present in the CreateKb DTO - - --save Save the kbId in config. - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/create.ts)_ - -## `bf qnamaker:kb:delete` - -Deletes a knowledge base by ID - -```text -USAGE - $ bf qnamaker:kb:delete - -OPTIONS - -h, --help qnamaker:kb:delete command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --force Do not prompt for confirmation, force the operation - - --kbId=kbId Knowledgebase id to be deleted. Overrides the knowledge base id present in the - config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/delete.ts)_ - -## `bf qnamaker:kb:export` - -Echoes a knowledge base JSON to stdout - -```text -USAGE - $ bf qnamaker:kb:export - -OPTIONS - -h, --help qnamaker:kb:export command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --environment=environment (required) Specifies whether environment is Test or Prod. - - --kbId=kbId Knowledgebase id to be exported. Overrides the knowledge base id present in the - config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/export.ts)_ - -## `bf qnamaker:kb:get` - -Gets metadata about a knowledgebase - -```text -USAGE - $ bf qnamaker:kb:get - -OPTIONS - -h, --help qnamaker:kb:get command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --kbId=kbId Knowledgebase id to get metadata. Overrides the knowledge base id present in the - config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/get.ts)_ - -## `bf qnamaker:kb:list` - -Lists all of your knowledgebases - -```text -USAGE - $ bf qnamaker:kb:list - -OPTIONS - -h, --help qnamaker:kb:list command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/list.ts)_ - -## `bf qnamaker:kb:publish` - -Publishes all unpublished in the knowledgebase to the prod endpoint. - -```text -USAGE - $ bf qnamaker:kb:publish - -OPTIONS - -h, --help qnamaker:kb:publish command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --kbId=kbId Knowledgebase id to pubish. Overrides the knowledge base id present in the config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/publish.ts)_ - -## `bf qnamaker:kb:replace` - -Replaces knowledgebase contents with new contents - -```text -USAGE - $ bf qnamaker:kb:replace - -OPTIONS - -h, --help qnamaker:kb:replace command help - -i, --in=in File path to the ReplaceKbDTO object to send in the body of the request - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --kbId=kbId Knowledgebase id. Overrides the knowledge base id present in the config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/replace.ts)_ - -## `bf qnamaker:kb:update` - -Adds or deletes QnA Pairs and / or URLs to an existing knowledge base - -```text -USAGE - $ bf qnamaker:kb:update - -OPTIONS - -h, --help qnamaker:kb:update command help - - -i, --in=in The file path to the UpdateKbOperationDTO object to send in the body of the - request. - - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - - --kbId=kbId Knowledgebase id. Overrides the knowledge base id present in the config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config - - --wait Wait for the operation to complete. -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/kb/update.ts)_ - -## `bf qnamaker:operationdetails` - -Command to get operation details - -```text -USAGE - $ bf qnamaker:operationdetails - -OPTIONS - -h, --help display qnamaker:operationdetails available commands -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/operationdetails/index.ts)_ - -## `bf qnamaker:operationdetails:get` - -Gets details of a specific long running operation. - -```text -USAGE - $ bf qnamaker:operationdetails:get - -OPTIONS - -h, --help qnamaker:operationdetails:get command help - --endpoint=endpoint Overrides public endpoint https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/ - --operationId=operationId (required) Operation id. - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in the config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/operationdetails/get.ts)_ - -## `bf qnamaker:query` - -Queries a knowledge base for an answer - -```text -USAGE - $ bf qnamaker:query - -OPTIONS - -h, --help qnamaker:query command help - --context=context Path to Context object json file with previous QnA - - --endpointKey=endpointKey Specifies the endpoint key for your private QnA service (From qnamaker.ai portal user - settings page). Overrides the value present in config - - --hostname=hostname Specifies the url for your private QnA service. Overrides the value present in config - - --kbId=kbId Specifies the active qnamaker knowledgebase id. Overrides the value present in the - config - - --qnaId=qnaId Exact qnaId to fetch from the knowledgebase, this field takes priority over question - - --question=question (required) Query to get a prediction for - - --scorethreshold=scorethreshold Specifies the confidence score threshold for the returned answer. - - --strictfilters=strictfilters Path to json file with MetadataDTO[] e.g {"strictfilters": MetadataDTO[]} - - --test Query against the test index - - --top=top Specifies the number of matching results -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/query.ts)_ - -## `bf qnamaker:train` - -Adds suggestions to the knowledgebase. - -```text -USAGE - $ bf qnamaker:train - -OPTIONS - -h, --help qnamaker:get:kb command help - - --endpointKey=endpointKey Specifies the endpoint key for your private QnA service.(from qnamaker.ai portal - user settings page). Overrides the value present in config. - - --hostname=hostname Specifies the url for your private QnA service. Overrides the value present in - config. - - --in=in File path to the FeedbackRecordDTO object to send in the body of the request. - - --kbId=kbId Specifies the active qnamaker knowledgebase id. Overrides the value present in the - config - - --subscriptionKey=subscriptionKey Specifies the qnamaker Ocp-Apim-Subscription Key (found in Keys under Resource - Management section for your Qna Maker cognitive service). Overrides the - subscriptionkey value present in config -``` - -_See code: [@microsoft/bf-qnamaker](https://github.com/microsoft/botframework-cli/tree/master/packages/qnamaker/src/commands/qnamaker/train.ts)_ - -## `bf qnamaker:translate` - -Translates given QnA Maker application JSON model or .qna file(s) - -```text -USAGE - $ bf qnamaker:translate - -OPTIONS - -f, --force If --out flag is provided with the path to an existing file, overwrites that file - -h, --help qnamaker:translate help - -i, --in=in Source .qna file(s) or QnA maker application JSON model - -o, --out=out Output folder name. If not specified stdout will be used as output - -r, --recurse Indicates if sub-folders need to be considered to find .qna file(s) - --srclang=srclang Source lang code. Auto detect if missing. - --tgtlang=tgtlang (required) Comma separated list of target languages. - --translate_comments When set, machine translate comments found in .qna file - --translate_link_text When set, machine translate link description in .qna file - --translatekey=translatekey (required) Machine translation endpoint key. -``` - -_See code: [@microsoft/bf-lu](https://github.com/microsoft/botframework-cli/tree/master/packages/lu/src/commands/qnamaker/translate.ts)_ - diff --git a/articles/v4sdk/bot-activity-handler-concept.md b/articles/v4sdk/bot-activity-handler-concept.md new file mode 100644 index 000000000..ad0b825fb --- /dev/null +++ b/articles/v4sdk/bot-activity-handler-concept.md @@ -0,0 +1,216 @@ +--- +title: Event-driven conversations and activity handlers +description: Become familiar with the bot activity handler. Learn about managing bot reasoning based on the type of activity received from a user. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Event-driven conversations using an activity handler + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +An _activity handler_ is an event-driven way to organize the conversational logic for your bot. +Each different type or subtype of activity represents a different type of conversational event. +Under the covers, the bot's _turn handler_ calls the individual activity handler for whatever type of activity it received. + +For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the _on message activity_ activity handler. When building your bot, your bot logic for handling and responding to messages will go in this _on message activity_ handler. Likewise, your logic for handling members being added to the conversation will go in your _on members added_ handler, which is called whenever a member is added to the conversation. + +For other ways to organize your bot logic, see the [bot logic](bot-builder-basics.md#bot-logic) section in **how bots work**. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + +# [C#](#tab/csharp) + +To implement your logic for these handlers, you'll override these methods in your bot, such as in the [sample activity handler](#sample-activity-handler) section below. For each of these handlers, there's no base implementation, so just add the logic that you want in your override. + +There are certain situations where you'll want to override the base turn handler, such as [saving state](bot-builder-concept-state.md) at the end of a turn. When doing so, be sure to first call `await base.OnTurnAsync(turnContext, cancellationToken);` to make sure the base implementation of `OnTurnAsync` is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as `OnMessageActivityAsync`. + +# [JavaScript](#tab/javascript) + +The JavaScript `ActivityHandler` uses an event emitter and listener pattern. +For example, use the `onMessage` method to register an event listener for message activities. You can register more than one listener. When the bot receives a message activity, the activity handler would see that incoming activity and send it each of the `onMessage` activity listeners, in the order in which they were registered. + +When building your bot, your bot logic for handling and responding to messages will go in the `onMessage` listeners. Likewise, your logic for handling members being added to the conversation will go in your `onMembersAdded` listeners, which are called whenever a member is added to the conversation. +To add these listeners, you'll register them in your bot as seen in the [Bot logic](bot-builder-basics.md#bot-logic) section below. For each listener, include your bot logic, then **be sure to call `next()` at the end**. By calling `next()`, you ensure that the next listener is run. + +Make sure to [save state](bot-builder-concept-state.md) before the turn ends. You can do so by overriding the activity handler `run` method and saving state after the parent's `run` method completes. + +There are no common situations where you'll want to override the base turn handler, so be careful if you try to do so. +There's a special handler called `onDialog`. The `onDialog` handler runs at the end, after the rest of the handlers have run, and isn't tied to a certain activity type. As with all the above handlers, be sure to call `next()` to ensure the rest of the process wraps up. + +# [Java](#tab/java) + +To implement your logic for these handlers, you'll override these methods in your bot, such as in the [sample activity handler](#sample-activity-handler) section below. There's no base implementation for each of these handlers, so add the logic you want in your override. + +There are certain situations where you'll want to override the base turn handler, such as [saving state](bot-builder-concept-state.md) at the end of a turn. When doing so, be sure first to call `super.onTurn(turnContext);` to make sure the base implementation of `onTurn` is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as `onMessageActivity`. + +# [Python](#tab/python) + +When building your bot, your bot logic for handling and responding to messages will go in this `on_message_activity` handler. Likewise, your logic for handling members being added to the conversation will go in your `on_members_added` handler, which is called whenever a member is added to the conversation. + +For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the `on_message_activity` activity handler. + +To implement your logic for these handlers, you'll override these methods in your bot, such as in the [sample activity handler](#sample-activity-handler) section below. For each of these handlers, there's no base implementation, so just add the logic that you want in your override. + +There are certain situations where you'll want to override the base turn handler, such as [saving state](bot-builder-concept-state.md) at the end of a turn. When doing so, be sure to first call `await super().on_turn(turnContext);` to make sure the base implementation of `on_turn` is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as `on_message_activity`. + +--- + +## Activity handling + +The bot logic processes incoming activities from one or more channels and generates outgoing activities in response. + +### [C#](#tab/csharp) + +The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from `ActivityHandler`, which implements the `IBot` interface. `ActivityHandler` defines various handlers for different types of activities, such as `OnMessageActivityAsync`, and `OnMembersAddedAsync`. These methods are protected, but can be overridden, since we're deriving from `ActivityHandler`. + +The handlers defined in `ActivityHandler` are: + +| Event | Handler | Description | +| :-- | :-- | :-- | +| Any activity type received | `OnTurnAsync` | Calls one of the other handlers, based on the type of activity received. | +| Message activity received | `OnMessageActivityAsync` | Override this to handle a `message` activity. | +| Conversation update activity received | `OnConversationUpdateActivityAsync` | On a `conversationUpdate` activity, calls a handler if members other than the bot joined or left the conversation. | +| Non-bot members joined the conversation | `OnMembersAddedAsync` | Override this to handle members joining a conversation. | +| Non-bot members left the conversation | `OnMembersRemovedAsync` | Override this to handle members leaving a conversation. | +| Event activity received | `OnEventActivityAsync` | On an `event` activity, calls a handler specific to the event type. | +| Token-response event activity received | `OnTokenResponseEventAsync` | Override this to handle token response events. | +| Non-token-response event activity received | `OnEventAsync` | Override this to handle other types of events. | +| Message reaction activity received | `OnMessageReactionActivityAsync` | On a `messageReaction` activity, calls a handler if one or more reactions were added or removed from a message. | +| Message reactions added to a message | `OnReactionsAddedAsync` | Override this to handle reactions added to a message. | +| Message reactions removed from a message | `OnReactionsRemovedAsync` | Override this to handle reactions removed from a message. | +| Installation update activity received | `OnInstallationUpdateActivityAsync` | On an `installationUpdate` activity, calls a handler based on whether the bot was installed or uninstalled. | +| Bot installed | `OnInstallationUpdateAddAsync` | Override this to add logic for when the bot is installed within an organizational unit. | +| Bot uninstalled | `OnInstallationUpdateRemoveAsync` | Override this to add logic for when the bot is uninstalled within an organizational unit. | +| Other activity type received | `OnUnrecognizedActivityTypeAsync` | Override this to handle any activity type otherwise unhandled. | + +These different handlers have a `turnContext` that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, `OnMessageActivityAsync` will always be handled, and is generally the most common. + +As in previous 4.x versions of this framework, there's also the option to implement the public method `OnTurnAsync`. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of `OnTurnAsync`, it's still an option. + +> [!IMPORTANT] +> If you do override the `OnTurnAsync` method, you'll need to call `base.OnTurnAsync` to get the base implementation to call all the other `OnAsync` handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run. + +### [JavaScript](#tab/javascript) + +The main bot logic is defined in the bot code. To implement a bot as an activity handler, extend `ActivityHandler`. `ActivityHandler` defines various events for different types of activities, and you can modify your bot's behavior by registering event listeners, such as with `onMessage` and `onConversationUpdate`. + +Use these methods to register listeners for each type of event: + +| Event | Registration method | Description | +| :-- | :-- | :-- | +| Any activity type received | `onTurn` | Registers a listener for when any activity is received. | +| Message activity received | `onMessage` | Registers a listener for when a `message` activity is received. | +| Conversation update activity received | `onConversationUpdate` | Registers a listener for when any `conversationUpdate` activity is received. | +| Members joined the conversation | `onMembersAdded` | Registers a listener for when members joined the conversation, including the bot. | +| Members left the conversation | `onMembersRemoved` | Registers a listener for when members left the conversation, including the bot. | +| Message reaction activity received | `onMessageReaction` | Registers a listener for when any `messageReaction` activity is received. | +| Message reactions added to a message | `onReactionsAdded` | Registers a listener for when reactions are added to a message. | +| Message reactions removed from a message | `onReactionsRemoved` | Registers a listener for when reactions are removed from a message. | +| Event activity received | `onEvent` | Registers a listener for when any `event` activity is received. | +| Token-response event activity received | `onTokenResponseEvent` | Registers a listener for when a `tokens/response` event is received. | +| Installation update activity received | `onInstallationUpdate` | Registers a listener for when any `installationUpdate` activity is received. | +| Bot installed | `onInstallationUpdateAdd` | Registers a listener for when the bot is installed within an organizational unit. | +| Bot uninstalled | `onInstallationUpdateRemove` | Registers a listener for when the bot is uninstalled within an organizational unit. | +| Other activity type received | `onUnrecognizedActivityType` | Registers a listener for when a handler for the specific type of activity isn't defined. | +| Activity handlers have completed | `onDialog` | Called after any applicable handlers have completed. | + +Call the `next` continuation function from each handler to allow processing to continue. If `next` isn't called, processing of the activity ends. + +### [Java](#tab/java) + +The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from `ActivityHandler`, which implements the `Bot` interface. `ActivityHandler` defines various handlers for different types of activities, such as `onMessageActivity`, and `onMembersAdded`. These methods are protected, but can be overridden, since we're deriving from `ActivityHandler`. + +The handlers defined in `ActivityHandler` are: + +| Event | Handler | Description | +| :-- | :-- | :-- | +| Any activity type received | `onTurn` | Calls one of the other handlers, based on the type of activity received. | +| Message activity received | `onMessageActivity` | Override this to handle a `message` activity. | +| Conversation update activity received | `onConversationUpdateActivity` | On a `conversationUpdate` activity, calls a handler if members other than the bot joined or left the conversation. | +| Non-bot members joined the conversation | `onMembersAdded` | Override this to handle members joining a conversation. | +| Non-bot members left the conversation | `onMembersRemoved` | Override this to handle members leaving a conversation. | +| Event activity received | `onEventActivity` | On an `event` activity, calls a handler specific to the event type. | +| Token-response event activity received | `onTokenResponseEvent` | Override this to handle token response events. | +| Non-token-response event activity received | `onEvent` | Override this to handle other types of events. | +| Message reaction activity received | `onMessageReactionActivity` | On a `messageReaction` activity, calls a handler if one or more reactions were added or removed from a message. | +| Message reactions added to a message | `onReactionsAdded` | Override this to handle reactions added to a message. | +| Message reactions removed from a message | `onReactionsRemoved` | Override this to handle reactions removed from a message. | +| Installation update activity received | `onInstallationUpdate` | On an `installationUpdate` activity, calls a handler based on whether the bot was installed or uninstalled. | +| Bot installed | `onInstallationUpdateAdd` | Override this to add logic for when the bot is installed within an organizational unit. | +| Bot uninstalled | `onInstallationUpdateRemove` | Override this to add logic for when the bot is uninstalled within an organizational unit. | +| Other activity type received | `onUnrecognizedActivityType` | Override this to handle any activity type otherwise unhandled. | + +These different handlers have a `turnContext` that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, `onMessageActivity` will always be handled, and is generally the most common. + +There's also the option to implement the public method `onTurn`. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of `onTurn`, it's still an option. + +> [!IMPORTANT] +> If you do override the `onTurn` method, you'll need to call `super.onTurn` to get the base implementation to call all the other `on` handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run. + +### [Python](#tab/python) + +The main bot logic is defined in the bot code. To implement a bot as an activity handler, derive your bot class from `ActivityHandler`, which in turn derives from the abstract `Bot` class. `ActivityHandler` defines various handlers for different types of activities, such as `on_message_activity` and `on_members_added`. These methods are protected, but can be overridden, since we're deriving from `ActivityHandler`. + +The handlers defined in `ActivityHandler` are: + +| Event | Handler | Description | +| :-- | :-- | :-- | +| Any activity type received | `on_turn` | Calls one of the other handlers, based on the type of activity received. | +| Message activity received | `on_message_activity` | Override this to handle a `message` activity. | +| Conversation update activity received | `on_conversation_update_activity` | On a `conversationUpdate` activity, calls a handler if members other than the bot joined or left the conversation. | +| Non-bot members joined the conversation | `on_members_added_activity` | Override this to handle members joining a conversation. | +| Non-bot members left the conversation | `on_members_removed_activity` | Override this to handle members leaving a conversation. | +| Event activity received | `on_event_activity` | On an `event` activity, calls a handler specific to the event type. | +| Token-response event activity received | `on_token_response_event` | Override this to handle token response events. | +| Non-token-response event activity received | `on_event_activity` | Override this to handle other types of events. | +| Message reaction activity received | `on_message_reaction_activity` | On a `messageReaction` activity, calls a handler if one or more reactions were added or removed from a message. | +| Message reactions added to a message | `on_reactions_added` | Override this to handle reactions added to a message. | +| Message reactions removed from a message | `on_reactions_removed` | Override this to handle reactions removed from a message. | +| Installation update activity received | `on_installation_update` | On an `installationUpdate` activity, calls a handler based on whether the bot was installed or uninstalled. | +| Bot installed | `on_installation_update_add` | Override this to add logic for when the bot is installed within an organizational unit. | +| Bot uninstalled | `on_installation_update_remove` | Override this to add logic for when the bot is uninstalled within an organizational unit. | +| Other activity type received | `on_unrecognized_activity_type` | Override this to handle any activity type otherwise unhandled. | + +These different handlers have a `turn_context` that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, `on_message_activity` will always be handled, and is generally the most common. + +As in previous 4.x versions of this framework, there's also the option to implement the public method `on_turn`. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of `on_turn`, it's still an option. + +> [!IMPORTANT] +> If you do override the `on_turn` method, you'll need to call `super().on_turn` to get the base implementation to call all the other `on_` handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run. + +--- + +## Sample activity handler + +For example, you can handle _on members added_ to welcome users to a conversation, and handle _on message_ to echo back messages they send to the bot. + +### [C#](#tab/csharp) + +[!code-csharp[C# activity handler](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/Bots/EchoBot.cs?range=12-31)] + +### [JavaScript](#tab/javascript) + +[!code-javascript[JavaScript activity handler](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/bot.js?range=6-29)] + +### [Java](#tab/java) + +[!code-java[Java activity handler](~/../botbuilder-samples/samples/java_springboot/02.echo-bot/src/main/java/com/microsoft/bot/sample/echo/EchoBot.java?range=25-47)] + +### [Python](#tab/python) + +[!code-python[Python activity handler](~/../botbuilder-samples/samples/python/02.echo-bot/bots/echo_bot.py?range=8-19)] + +--- + +## Next steps + +- The Microsoft Teams channel introduces some Teams-specific activities that your bot will need to support to work properly with Teams. To understand key concepts of developing bots for Microsoft Teams, see [How Microsoft Teams bots work](bot-builder-basics-teams.md) +- An activity handler is a good way to design a bot that doesn't need to track conversational state between turns. The [dialogs library](bot-builder-concept-dialog.md) provides ways to manage a long-running conversation with the user. diff --git a/articles/v4sdk/bot-builder-authentication-basics.md b/articles/v4sdk/bot-builder-authentication-basics.md new file mode 100644 index 000000000..e8fbdc9a4 --- /dev/null +++ b/articles/v4sdk/bot-builder-authentication-basics.md @@ -0,0 +1,66 @@ +--- +title: Bot Framework SDK authentication basics +description: Learn about the authentication basics in the Bot Framework SDK. Learn how a bot can access protected resources on behalf of a user. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: quickstart +ms.custom: + - evergreen +--- + +# Bot Framework authentication basics + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Often a bot must access protected resources, for example email account, on behalf of the user. In order to do that the bot must be **authorized** based on the user's credentials. Before that, the user must be **authenticated** first. +The bot must be a known entity, that is, it must be authenticated within the Azure AI Bot Service context. This happens prior to the bot being authorized to operate on behalf of the user. + +Let's see if we can untangle this bundle by starting with a bird's eye view of the Bot Framework authentication context. + +:::image type="content" source="media/concept-bot-authentication/bot-auth-context2.png" alt-text="Bot authentication context"::: + +- When you register a bot in Azure via a **Azure Bot** resource, Azure creates an Microsoft Entra ID registration application. This application has an app ID (`MicrosoftAppId`) and a client secret (`MicrosoftAppPassword`). You use these values in the bot configuration files as described below. + +- Microsoft Entra ID is a cloud identity service that allows you to build applications that securely sign in _users_ using industry standard protocols like OAuth 2.0. You create an Active Directory application and use its _app ID_ and _password_ to select an _identity provider_ and generate an _authentication_ connection. You add this connection to your bot resource. You also add the connection name in the bot configuration files as described below. + +- A bot is identified by its Azure Bot resource _app ID_ and _password_. You add the related values in the bot's configuration file or to a secrets or key manager. You also add the connection name. The bot uses a token based on the app ID and password to access protected resources. The bot uses different tokens, based on the authentication connection, to access the user's protected resources. + +## Bot authentication and authorization + +The following are the main steps to authenticate a bot and authorize it to access user's protected resources: + +1. Create a bot channel registration application. +1. Add the registration app ID and password to the bot configuration file. This allows the bot to be authenticated to access protected resources. +1. Create an Microsoft Entra ID application to select an identity provider to authenticate the user. +1. Create an authentication connection and add it to the channel registration settings. +1. Add the connection name to the bot's configuration files. This allows the bot to be authorized to access user's protected resources. + +For a complete example, see [Add authentication to a bot](bot-builder-authentication.md). + +### Best practices + +- Keep the Microsoft Entra ID app registration restricted to its original purpose of service to service application. +- Create an additional Microsoft Entra ID app for any user to service authentication, for more finite control over disabling authentication connections, rolling secrets, or reusing the Microsoft Entra ID app with other applications. + +Some of the problems you encounter if you also use the Microsoft Entra ID registration app for authentication are: + +- If the certificate attached to the Microsoft Entra ID app registration needs to be renewed, it would impact users that have authenticated with other Microsoft Entra ID services using the certificate. +- In general, it creates a single point of failure and control for all authentication-related activities with the bot. + +## Related topics + +The following articles provide in-depth information and examples about authentication within the Bot Framework. Start by looking at the [Authentication types](bot-builder-concept-authentication-types.md) and then [Identity providers](bot-builder-concept-identity-providers.md). + +| Article | Description | +|--|--| +| [Authentication types](bot-builder-concept-authentication-types.md) | Describes the two Bot Framework authentication types and the tokens they use. | +| [Identity providers](bot-builder-concept-identity-providers.md) | Describes the use of identity providers. They allow you to build applications that securely sign in users using industry standard protocols like OAuth2.0. | +| [User authentication](bot-builder-concept-authentication.md) | Describes user's authentication and the related token to authorize a bot to perform tasks on the user's behalf. | +| [Single sign on](bot-builder-concept-sso.md) | Describes single user authentication for multiple protected resources access. | +| [Register a bot with Azure](../bot-service-quickstart-registration.md) | Shows how to register a bot with the Azure AI Bot Service. | +| [Bot Framework security guidelines](bot-builder-security-guidelines.md) | Describes security in general and as it applies to the Bot Framework. | +| [Add authentication to a bot](bot-builder-authentication.md) | Shows how to create bot channel registration, create an authentication connection, and prepare the code. | +| [Add single sign on to a bot](bot-builder-authentication-sso.md) | Shows how to add single sign-on authentication to a bot. | diff --git a/articles/v4sdk/bot-builder-authentication-federated-credential.md b/articles/v4sdk/bot-builder-authentication-federated-credential.md new file mode 100644 index 000000000..a2445d635 --- /dev/null +++ b/articles/v4sdk/bot-builder-authentication-federated-credential.md @@ -0,0 +1,502 @@ +--- +title: Implement Authentication with Federated Identity Credentials +description: Learn how to integrate user authentication using federated identity credentials +author: kparihar +ms.author: kparihar +manager: kunsingh +ms.reviewer: kunsingh +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen +monikerRange: 'azure-bot-service-4.0' +--- + + + +# Implement Authentication with Federated Identity Credentials + + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +For an overview of how the Bot Framework handles this kind of authentication, see [User authentication](bot-builder-concept-authentication.md). + +This article covers how to: + +- Create an Azure Bot resource. +- Create the Microsoft Entra ID identity provider and use federated credentials flow. +- Register the Microsoft Entra ID identity provider with the bot for user authentication using federated credential flow. +- Prepare the bot code. + +Once you finish this article, you have a bot that can respond to a few simple tasks. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + + +## Prerequisites + +- Knowledge of [bot basics][concept-basics], [managing state][concept-state], the [dialogs library][concept-dialogs], and +how to [implement sequential conversation flow][simple-dialog], and how to [reuse dialogs][component-dialogs]. +- Knowledge of Azure and OAuth 2.0 development. +- Visual Studio 2017 or later for .NET. +- Node.js for JavaScript. +- Python [3.8+](https://www.python.org/downloads/release/python-383/) for Python. +- One of the samples listed. + + | Sample | BotBuilder version | Demonstrates | + |:---|:---:|:---| + | **Authentication** in [**C#**][cs-auth-sample]| v4 | OAuthCard support | + + To run the samples referenced in this article, you need: + + - A Microsoft Entra ID application with which to register a bot resource in Azure. This application allows the bot to access an external secured resource, such as Microsoft Graph. It also allows the user to communicate with the bot via several channels such as Web Chat. + - A separate Microsoft Entra ID application to function as the identity provider. This application provides the credentials needed to establish an OAuth connection between the bot and the secured resource. Notice that this article uses Active Directory as an identity provider. Many other providers are also supported. + +> [!IMPORTANT] +> Whenever you register a bot in Azure, it gets assigned an Microsoft Entra ID application. However, this application secures channel-to-bot access. You need an additional Microsoft Entra ID application for each external secured resource you want the bot to access on behalf of the user. + + +## Create the Azure Bot resource + + +Create the **Azure Bot** resource, which allows you to register your bot with the Azure AI Bot Service. + +[!INCLUDE [bot-resource-type-tip](../includes/bot-resource-type-tip.md)] + +1. Go to the [Azure portal](https://portal.azure.com/). +1. In the right pane, select **Create a resource**. +1. In the search box enter `bot`, then press **Enter**. +1. Select the **Azure Bot** card. + + :::image type="content" source="../media/azure-manage-a-bot/azure-bot-resource.png" alt-text="Select Azure bot resource"::: + +1. Select **Create**. +1. Enter values in the required fields and review and update settings. + + 1. Provide information under **Project details**. Select whether your bot has global or local data residency. Currently, the local data residency feature is available for resources in the "westeurope" and "centralindia" region. For more information, see [Regionalization in Azure AI Bot Service](bot-builder-concept-regionalization.md). + + :::image type="content" source="../media/azure-bot-resource/azure-bot-project-details.png" alt-text="The project details settings for an Azure Bot resource"::: + + 1. Provide information under **Microsoft App ID**. Select how your bot identity is managed in Azure and whether to create a new identity or use an existing one. + + :::image type="content" source="../media/azure-bot-resource/azure-bot-ms-app-st.png" alt-text="The Microsoft app ID settings for an Azure Bot resource"::: + +1. Select **Review + create**. +1. If the validation passes, select **Create**. +1. Once the deployment completes, select **Go to resource**. You should see the bot and related resources listed in the resource group you selected. +1. If you don't already have the Bot Framework SDK, select **Download from GitHub** to learn how to consume the packages for your preferred language. + + :::image type="content" source="../media/azure-manage-a-bot/azure-bot-create-sdk.png" alt-text="Create bot in SDK"::: + +You're now ready to build your bot with the Bot Framework SDK. + + +### Create a User Assigned Managed Identity + +Create the **User Assigned Managed Identity** resource, which allows you to set up an identity that is used as a trust mechanism to obtain access tokens from the Microsoft Entra application. + + +1. Go to the [Azure portal](https://portal.azure.com/). +1. In the right pane, select **Create a resource**. +1. In the search box enter `Managed Identities`, then press **Enter**. +1. Select the **User Assigned Managed Identity** card. + + :::image type="content" source="../media/azure-manage-a-bot/managed-identity.png" alt-text="Select User Assigned Managed Identity"::: + +1. Select **Create**. +1. Enter values in the required fields and review and update settings. + + 1. Provide information under **Project details**. Select whether your identity has global or local data residency. + + :::image type="content" source="../media/azure-manage-a-bot/user-mi-create.png" alt-text="The project details settings for an Managed Identity"::: + +1. Select **Review + create**. +1. If the validation passes, select **Create**. +1. Once the deployment completes, select **Go to resource**. You should see the managed identity and related resources listed in the resource group you selected. + + +### To create a new Federated Credentials + +The owner of the bot's App Service resource can add a new trust: + +1. Go to the Azure Bot resource blade for your bot. +1. Go to the bot's **Configuration** blade. +1. Select **Manage**, next to **Microsoft App ID**, to go to the **Certificates + secrets** blade for the app service. +1. On the **Certificates + secrets** blade, select the **Federated Credentials** tab and Add Credentials (+). + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds.png" alt-text="Create a Federated Credentials."::: + +1. On Add Credentials page, Choose the **Federated credential scenario** to **Customer Managed Keys** + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-mi.png" alt-text="Select Federated Credentials Scenario."::: + +1. Enter values in the required fields and review and update settings + 1. Provide information under **Select a managed identity**. + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-select.png" alt-text="Select a Managed Identity Section"::: + 1. On the **Select a managed identity** blade: + 1. Select your subscription. + 1. For **User assigned managed identities**, select the managed identity created earlier. + 1. Select **Select** to use this identity for your bot. + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-select-search.png" alt-text="Select a Managed Identity"::: + 1. Provide information under **Credential details**. + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-cred.png" alt-text="Enter Credential details"::: + +1. Select **Add** to add the credential. + + + + +### To update your Bot + +> [!NOTE] +> The Bot Framework SDK version 4.22.8 or later supports the use of Federated credentials. + + +1. Upgrade the Bot Framework SDK version to 4.22.8 or later. +1. To enable the Federated Credential for your bot, add the following code to your Startup.cs file: + + ```csharp + services.AddSingleton(new FederatedServiceClientCredentialsFactory( + Configuration["MicrosoftAppId"], + Configuration["MicrosoftAppClientId"], + Configuration["MicrosoftAppTenantId"])); + ``` + +1. Follow these steps to include identity information in your bot’s configuration file. The specific file and steps might vary depending on the programming language you're using to create the bot. + + > [!IMPORTANT] + > The Java version of the Bot Framework SDK only supports multi-tenant bots. + > The C#, JavaScript, and Python versions support all three application types for managing the bot's identity. + + | Language | File name | Notes | + |:-----------|:-----------------------|:--------------------------------------------------------------------------------------------------------------------| + | C# | appsettings.json | Supports all three application types for managing your bot's identity. | + | JavaScript | .env | Supports all three application types for managing your bot's identity. | + | Java | application.properties | Only supports multitenant bots. | + | Python | config.py | Supports all three application types for managing your bot's identity. | + +The identity information you need to add depends on the bot's application type. +Provide the following values in your configuration file. + +| Property | Value | +|:-----------------------|:-------------------------| +| `MicrosoftAppType` | `SingleTenant/MultiTenant` | +| `MicrosoftAppId` | The bot's app ID. | +| `MicrosoftAppClientId` | The User Managed Identity password. | +| `MicrosoftAppTenantId` | The bot's app tenant ID. | + + + +### To get your app or tenant ID + +To get your bot's app or tenant ID: + +1. Go to the Azure Bot resource blade for your bot. +1. Go to the bot's **Configuration** blade. + From this blade, you can copy the bot's **Microsoft App ID** or **App Tenant ID**. + + +### To update your app service + +If your bot uses an existing App Service resource (web app) and is a _single_ or _multitenant_ application, you might need to update its app service. + +1. Go to the App Service blade for your bot's web app. +1. Under **Settings**, select **Identity**. +1. On the **Identity** blade, select the **User assigned** tab and **Add** (+). +1. On the **Add user assigned managed identity** blade: + 1. Select your subscription. + 1. For **User assigned managed identities**, select the managed identity created earlier. + 1. Select **Add** to use this identity for your bot. + + :::image type="content" source="../media/how-to-create-single-tenant-bot/app-service-managed-identity.png" alt-text="The App Service Identity blade with the managed identity for the bot selected."::: + + + + + + +## Microsoft Entra ID identity service + +The Microsoft Entra ID is a cloud identity service that allows you to build applications that securely sign in users using industry standard protocols like OAuth 2.0. +1. Microsoft identity platform (v2.0). Also known as the **Microsoft Entra ID** endpoint, which is an evolution of the Azure AD platform (v1.0).It lets you build applications that sign in to all Microsoft identity providers and obtain tokens to call Microsoft APIs, like Microsoft Graph, or other developer-built APIs. For more information, see the [Microsoft identity platform (v2.0) overview](/azure/active-directory/develop/active-directory-appmodel-v2-overview). + +For information about the differences between the v1 and v2 endpoints, see [Why update to Microsoft identity platform (v2.0)?](/azure/active-directory/develop/active-directory-v2-compare). For complete information, see [Microsoft identity platform (formerly Microsoft Entra ID for developers)](/azure/active-directory/develop/). + + + +### Create the Microsoft Entra ID identity provider + +This section shows how to create a Microsoft Entra ID identity provider that uses OAuth 2.0 to authenticate the bot. You can use Microsoft Entra ID endpoints. + +> [!TIP] +> You'll need to create and register the Microsoft Entra ID application in a tenant in which you can consent to delegate permissions requested by an application. + +1. Open the [Microsoft Entra ID][azure-aad-blade] panel in the Azure portal. + If you aren't in the correct tenant, select **Switch directory** to switch to the correct tenant. (For information on how to create a tenant, see [Access the portal and create a tenant](/azure/active-directory/fundamentals/active-directory-access-create-new-tenant).) +1. Open the **App registrations** panel. +1. In the **App registrations** panel, select **New registration**. +1. Fill in the required fields and create the app registration. + + 1. Name your application. + 1. Select the **Supported account types** for your application. (Any of these options work with this sample.) + 1. For the **Redirect URI**, select **Web** and set the URL to one of the [supported OAuth redirect URLs](../ref-oauth-redirect-urls.md). + + 1. Select **Register**. + + - Once created, Azure displays the **Overview** page for the app. + - Record the **Application (client) ID** value. You use this value later as the _client ID_ when you create the connection string and register the Microsoft Entra ID provider with the bot registration. + - Record the **Directory (tenant) ID** value. You use this value to register this provider application with your bot. + +1. In the navigation pane, select **Certificates & secrets** to create a secret for your application. + 1. Under **Federated Credentials**, select **Add Credentials**. + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds.png" alt-text="Create a Federated Credentials."::: + + 1. On Add Credentials page, Choose the **Federated credential scenario** to **Other Issuer** + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-others.png" alt-text="Select Federated Credentials Other Issuer Scenario."::: + + 1. Enter values in the required fields and review and update settings + 1. Provide information under **Connect your account**. + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-others-account.png" alt-text="Connect your account"::: + + 1. **_Issuer_** : `https://login.microsoftonline.com/{customer-tenant-ID}/v2.0` + 1. **_Subject Identifier_** : /eid1/c/pub/t/{base64 encoded customer tenant ID}/a/{base64 encoded first-party app client ID}/{unique-identifier-for-projected-identity} + - The following table contains Base64url encoded byte-array representation of supported first-party application IDs. Use this value which represents our first-party app. + + | Encoded Value | Description | + |--|--| + |9ExAW52n_ky4ZiS_jhpJIQ |Base64url encoded of Bot Service Token Store| + |ND1y8_Vv60yhSNmdzSUR_A |Base64url encoded of Bot Framework Dev Portal| + + - The following [sample code](https://dotnetfiddle.net/p11CFZ) assists you in converting your tenant ID to a Base64url encoded byte-array representation. Please use the value that corresponds to the tenant of your application + + 1. **_Audience_** : api://AzureADTokenExchange (Use Cloud specific values) + 1. **_Unique-identifier-for-projected-identity_** : The token has the same value specified as the Unique Identifier in the OAuth Connection Setting. + + 1. Provide information under **Credential details**. + + :::image type="content" source="../media/azure-manage-a-bot/entra-fic-creds-scenario-cred.png" alt-text="Enter Credential details"::: + + 1. Select **Add** to add the credential. + + +1. In the navigation pane, select **API permissions** to open the **API permissions** panel. It's a best practice to explicitly set the API permissions for the app. + + 1. Select **Add a permission** to show the **Request API permissions** pane. + 1. For this sample, select **Microsoft APIs** and **Microsoft Graph**. + 1. Choose **Delegated permissions** and make sure the permissions you need are selected. This sample requires theses permissions. + + > [!NOTE] + > Any permission marked as **ADMIN CONSENT REQUIRED** will require both a user and a tenant admin to login, so for your bot tend to stay away from these. + - **openid** + - **profile** + - **Mail.Read** + - **Mail.Send** + - **User.Read** + - **User.ReadBasic.All** + + 1. Select **Add permissions**. (The first time a user accesses this app through the bot, they need to grant consent.) + +You now have a Microsoft Entra ID application configured. + + +> [!NOTE] +> You'll assign the **Application (client) ID**, when you create the connection string and register the identity provider with the bot registration. See next section. + +### Register the Microsoft Entra ID identity provider with the bot + +The next step is to register your identity provider with your bot. +> [!NOTE] +> Single Tenant Entra Application is only support for **AAD v2 with Federated Credentials** service provider. +> Support for multi-tenant apps will be added in future. + +1. Open your bot's Azure Bot resource page in the [Azure portal][azure-portal]. +1. Select **Settings**. +1. Under **OAuth Connection Settings** near the bottom of the page, select **Add Setting**. +1. Fill in the form as follows: + + 1. **Name**. Enter a name for your connection. You use it in your bot code. + 1. **Service Provider**. Select **AAD v2 with Federated Credentials** to display service provider specific fields. + 1. **Client id**. Enter the application (client) ID you recorded for your Microsoft Entra ID identity provider(**Only Single Tenant Supported**). + 1. **Unique Identifier**. Enter the unique identifier you recorded for your Microsoft Entra ID identity provider while creating federated credentials. + 1. **Token Exchange URL**. Leave it blank because it's used for SSO in Microsoft Entra ID only. + 1. **Tenant ID**. Enter the **directory (tenant) ID** that you recorded earlier for your Microsoft Entra ID app or **common** depending on the supported account types selected when you created the Azure DD app. To decide which value to assign, follow these criteria: + + - When creating the Microsoft Entra ID app, if you selected **Accounts in this organizational directory only (Microsoft only - Single tenant)**, enter the tenant ID you recorded earlier for the Microsoft Entra ID app. + - However, if you selected **Accounts in any organizational directory (Any Microsoft Entra ID directory - Multi tenant and personal Microsoft accounts e.g. Xbox, Outlook.com)** or **Accounts in any organizational directory(Microsoft Entra ID directory - Multi tenant)**, enter `common` instead of a tenant ID. Otherwise, the Microsoft Entra ID app verifies through the tenant whose ID was selected and exclude personal Microsoft accounts. + + This is the tenant associated with the users who can be authenticated. For more information, see [Tenancy in Microsoft Entra ID](/azure/active-directory/develop/single-and-multi-tenant-apps). + + 1. For **Scopes**, enter the names of the permission you chose from the application registration. For testing purposes, you can just enter: + `openid profile`. + + > [!NOTE] + > For Microsoft Entra ID, **Scopes** field takes a case-sensitive, space-separated list of values. + +1. Select **Save**. + +--- + +### Test your connection + +1. Select on the connection entry to open the connection you created. +1. Select **Test Connection** at the top of the **Service Provider Connection Setting** pane. +1. The first time, this should open a new browser tab listing the permissions your app is requesting and prompt you to accept. +1. Select **Accept**. +1. This should then redirect you to a **Test Connection to \ Succeeded** page. + +You can now use this connection name in your bot code to retrieve user tokens. + +## Prepare the bot code + +You need your bot's app ID and password to complete this process. + +### [C#](#tab/csharp) + +1. Clone from the GitHub repository the sample you want to work with: [**Bot authentication**][cs-auth-sample]. +1. Update **appsettings.json**: + + - Set `ConnectionName` to the name of the OAuth connection setting you added to your bot. + - Set `MicrosoftAppId` and `MicrosoftAppClientId` to your bot's app ID and app secret. + + [!code-json[appsettings](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/appsettings.json)] + + To use OAuth in bot with data-residency in public cloud, you must add the following configurations in your appsettings + ```json + "OAuthUrl": "", + "ToChannelFromBotOAuthScope": "https://api.botframework.com", + "ToChannelFromBotLoginUrlTemplate": "https://api.botframework.com", + "PublicAzureChannel": "https://api.botframework.com", + "ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.com/v1/.well-known/openidconfiguration", + "ToBotFromEmulatorOpenIdMetadataUrl": "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", + "ToBotFromChannelTokenIssuer": "https://api.botframework.com", + "ToChannelFromBotLoginUrl": "https://login.microsoftonline.com/botframework.com", + ``` + + Where _\_ is one of the following URIs: + + |URI|Description| + |:-|:-| + |`https://europe.api.botframework.com`|For public-cloud bots with data residency in Europe.| + |`https://unitedstates.api.botframework.com`|For public-cloud bots with data residency in the United States.| + |`https://india.api.botframework.com`|For public-cloud bots with data residency in India.| + +1. Update **Startup.cs**: + + To use OAuth in _non-public Azure clouds_, like the government cloud, you must add the following code in the **Startup.cs** file. + + ```csharp + string uri = ""; + MicrosoftAppCredentials.TrustServiceUrl(uri); + OAuthClientConfig.OAuthEndpoint = uri; + + ``` + + Where _\_ is one of the following URIs: + + |URI|Description| + |:-|:-| + |`https://api.botframework.azure.us`|For United States government-cloud bots without data residency.| + |`https://api.botframework.com`|For public-cloud bots without data residency. This is the default URI and doesn't require a change to **Startup.cs**.| + +> [!NOTE] +> You could now publish the bot code to your Azure subscription (right-select on the project and choose **Publish**), but it's not necessary for this article. You would need to set up a publishing configuration that uses the application and hosting plan that you used when configuration the bot in the Azure portal. + + +### Testing + +After you configured the authentication mechanism, you can perform the actual bot sample testing. + +> [!NOTE] +> You may be asked to enter a _magic code_, because the way the bot sample is implemented. This magic code is part of the [RFC#7636](https://tools.ietf.org/html/rfc7636#page-5) and is there to add an extra security element. By removing the magic code, there's an increased security risk. This can be mitigated using Direct Line with enhanced authentication enabled. For more information, see [Bot Framework enhanced authentication](bot-builder-security-enhanced.md). + +## Authentication example + +In the **Bot authentication** sample, the dialog is designed to retrieve the user token after the user is logged in. + +:::image type="content" source="media/how-to-auth/auth-bot-test.png" alt-text="Sample conversation with the authentication sample bot."::: + +--- + +## Additional information + +When a user asks the bot to do something that requires the bot to have the user logged in, the bot can use an `OAuthPrompt` to initiate retrieving a token for a given connection. The `OAuthPrompt` creates a token retrieval flow that consists of: + +1. Checking to see if the Azure AI Bot Service already has a token for the current user and connection. If there's a token, the token is returned. +1. If Azure AI Bot Service doesn't have a cached token, an `OAuthCard` is created which is a sign-in button the user can select on. +1. After the user selects on the `OAuthCard` sign-in button, Azure AI Bot Service will either send the bot the user's token directly or present the user with a six-digit authentication code to enter in the chat window. +1. If the user is presented with an authentication code, the bot then exchanges this authentication code for the user's token. + +The following sections describe how the sample implements some common authentication tasks. + +### Use an OAuth prompt to sign the user in and get a token + +### [C#](#tab/csharp) + +:::image type="content" source="media/how-to-auth/architecture.png" alt-text="Architecture diagram for the C# sample."::: + +**Dialogs\MainDialog.cs** + +Add an OAuth prompt to **MainDialog** in its constructor. Here, the value for the connection name was retrieved from the **appsettings.json** file. + +[!code-csharp[Add OAuthPrompt](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/Dialogs/MainDialog.cs?range=23-31)] + +Within a dialog step, use `BeginDialogAsync` to start the OAuth prompt, which asks the user to sign in. + +- If the user is already signed in, a token response event is generated without prompting the user. Otherwise, the user is prompted to sign in. After the user attempts to sign in, the Azure AI Bot Service sends the token response event. + +[!code-csharp[Use the OAuthPrompt](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/Dialogs/MainDialog.cs?range=49)] + +Within the following dialog step, check for the presence of a token in the result from the previous step. If it's not null, the user successfully signed in. + +[!code-csharp[Get the OAuthPrompt result](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/Dialogs/MainDialog.cs?range=54-56)] + +--- + +### Wait for a TokenResponseEvent + +When you start an OAuth prompt, it waits for a token response event, from which it retrieves the user's token. + +### [C#](#tab/csharp) + +**Bots\AuthBot.cs** + +**AuthBot** derives from `ActivityHandler` and explicitly handles token response event activities. Here, we continue the active dialog, which allows the OAuth prompt to process the event and retrieve the token. + +[!code-csharp[OnTokenResponseEventAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/Bots/AuthBotFIC.cs?range=31-37)] + +--- + +### Log the user out + +It's best practice to let users explicitly sign out, instead of relying on the connection to time out. + +### [C#](#tab/csharp) + +**Dialogs\LogoutDialog.cs** + +[!code-csharp[Allow sign out](~/../botbuilder-samples/samples/csharp_dotnetcore/86.bot-authentication-fic/Dialogs/LogoutDialog.cs?range=45-63&highlight=11)] + +--- + + +### Further reading + +- [Bot Framework other resources](../bot-service-resources-links-help.md) includes links for more support. +- The [Bot Framework SDK](https://github.com/microsoft/botbuilder) repo has more information about repos, samples, tools, and specs associated with the Bot Builder SDK. + + +[azure-portal]: https://ms.portal.azure.com +[azure-aad-blade]: https://ms.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview + +[concept-basics]: bot-builder-basics.md +[concept-state]: bot-builder-concept-state.md +[concept-dialogs]: bot-builder-concept-dialog.md + +[simple-dialog]: bot-builder-dialog-manage-conversation-flow.md +[component-dialogs]: bot-builder-compositcontrol.md + +[cs-auth-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/86.bot-authentication-fic diff --git a/articles/v4sdk/bot-builder-authentication-sso.md b/articles/v4sdk/bot-builder-authentication-sso.md new file mode 100644 index 000000000..628225c68 --- /dev/null +++ b/articles/v4sdk/bot-builder-authentication-sso.md @@ -0,0 +1,317 @@ +--- +title: Add single sign-on to a bot +description: Learn how to add single sign-on (SSO) to your bot to reduce the number of times your users need to sign in to other services. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Add single sign-on to a bot + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article shows how to use the single sign-on (SSO) feature in a bot. +To do so, this feature uses a _consumer_ bot—also known as the _root_ or _parent_ bot—to interact with a _skill_ or _child_ bot. +This article uses the terms root bot and skill bot. + +If you include SSO support, a user can sign into the root bot using an identity provider and won't need to sign in again when control passes to a skill. + +The root and skill bots are separate bots, running on potentially different servers, each with its own separate memory and state. +For more information about skills, see [Skills overview](skills-conceptual.md) and [Implement a skill](skill-implement-skill.md). +For more information about user authentication, see [Bot Framework authentication basics](bot-builder-authentication-basics.md), [User authentication](bot-builder-concept-authentication.md), and [Add authentication to a bot](bot-builder-authentication.md). + +> [!IMPORTANT] +> When you use Azure AI Bot Service authentication with _Web Chat_, there are some important security considerations you must keep in mind. For more information, see the [security considerations](../rest-api/bot-framework-rest-direct-line-3-0-authentication.md#security-considerations) section in the REST authentication article. + +## Prerequisites + +- Knowledge of [Bot basics][concept-basics], [Managing state][concept-state], and [About single sign-on](bot-builder-concept-sso.md). +- Knowledge of [The dialogs library][concept-dialogs] and how to [implement sequential conversation flow][simple-dialog] and [reuse dialogs][component-dialogs] +- Knowledge of Azure and OAuth 2.0 development. +- Visual Studio 2019 or later for .NET. +- The SSO with simple skill consumer and skill in [C#][cs-auth-sample]. + +## About the sample + +This article references two bots: the **RootBot** and the **SkillBot**. The **RootBot** forwards activities to the **SkillBot**. They model this _typical_ skill scenario: + +- A _root_ bot calls one or more skill bots. +- Both the root and skill bots implement the basic authentication described in the [Add authentication to a bot](bot-builder-authentication.md) article. +- The user logs into root bot. +- Because of the SSO and being already logged into the root bot, they're logged into the skill bot without requiring user interaction again. + +For an overview of how the Bot Framework handles authentication, see [User authentication](bot-builder-concept-authentication.md). +For SSO background information, see [Single sign-on](bot-builder-concept-sso.md). + +The **RootBot** supports SSO. It communicates with the **SkillBot** on behalf of the user, without the user being required to authenticate again into the _SkillBot. + +For each project in the sample, you need the following: + +1. An Microsoft Entra ID application to register a bot resource in Azure. +1. An Microsoft Entra ID identity provider application for authentication. + > [!NOTE] + > Currently, only the [Microsoft Entra ID](/azure/bot-service/bot-builder-concept-identity-providers?view=azure-bot-service-4.0&tabs=adv2%2Cga2#azure-ad-v2-1&preserve-view=true) identity provider is supported. + +## Create the Azure RootBot resource + +1. Create an Azure bot resource in the [Azure portal][azure-portal] for the `RootBot`. Follow the steps described in +[Create an Azure bot resource](bot-builder-authentication.md#create-the-resource). +1. Copy and save the bot registration **app ID** and the **client secret**. + +## Create the Microsoft Entra ID identity for RootBot + +The Microsoft Entra ID is a cloud identity service that allows you to build applications that securely sign in users using industry standard protocols like OAuth2.0. + +1. Create an identity application for the `RootBot` that uses Microsoft Entra ID to authenticate the user. Follow the steps described in [Create the Microsoft Entra ID identity provider](bot-builder-authentication.md#create-the-microsoft-entra-id-identity-provider). + +1. In the left pane, select **Manifest**. +1. Set `accessTokenAcceptedVersion` to 2. +1. Select **Save**. +1. In the left pane, select **Expose an API**. +1. In the right pane, select **Add a scope**. +1. On the far right _Add a scope_ section, select **Save and continue**. +1. In the displayed window, under _Who can consent?_, select **Admins and users**. +1. Enter the remaining required information. +1. Select **Add scope**. +1. Copy and save the scope value. + + + +### Create an OAuth connection setting for RootBot + +1. Create an Microsoft Entra ID connection in the `RootBot` bot registration and enter values as described in [Microsoft Entra ID](/azure/bot-service/bot-builder-concept-identity-providers?view=azure-bot-service-4.0&tabs=adv2%2Cga2#azure-ad-v2-1&preserve-view=true) and the value described below. + +1. Leave the **Token Exchange URL** empty. +1. In the **Scopes** box, enter the `RootBot` scope value you saved in the previous steps. + > [!NOTE] + > _Scopes_ contains the URL that the user initially signs in into the root bot, while _token exchange URL_ is left empty. + > + > As an example, let's assume that the root bot _appid_ is _rootAppId_ and the skill bot _appid_ is _skillAppId_. The root bot's _scopes_ will look like _api://rootAppId/customScope_, which is used to login the user. This root bot's _scopes_ is then exchanged with _api://skillAppId/customscope_ during SSO. +1. Copy and save the name of the connection. + +## Create the Azure SkillBot resource + +1. Create an Azure bot resource in the [Azure portal][azure-portal] for the `SkillBot`. Follow the steps described in +[Create an Azure bot resource](bot-builder-authentication.md#create-the-resource). +1. Copy and save the bot registration **app ID** and the **client secret**. + +## Create the Microsoft Entra ID identity for SkillBot + +The Microsoft Entra ID is a cloud identity service that allows you to build applications that securely sign in users using industry standard protocols like OAuth2.0. + +1. Create an identity application for the `SkillBot` that uses Microsoft Entra ID to authenticate the bot. Follow the steps described in [Create the Microsoft Entra ID identity provider](bot-builder-authentication.md#create-the-microsoft-entra-id-identity-provider). + +1. In the left pane, select **Manifest**. +1. Set `accessTokenAcceptedVersion` to 2. +1. Select **Save**. +1. In the left pane, select **Expose an API**. +1. In the right pane, select **Add a scope**. +1. In the far right **Add a scope** section, select **Save and continue**. +1. In the displayed window, under _Who can consent?_ select **Admins and users**. +1. Enter the remaining required information. +1. Select **Add scope**. +1. Copy and save the scope value. +1. Select **Add a client application**. In the far right section, in the **Client ID** box, enter the **RootBot identity** app ID you saved before. Make sure you use the _RootBot_ identity and not the registration app ID. + + > [!NOTE] + > For client applications, Azure AI Bot Service does not support single sing-on with the Microsoft Entra ID B2C identity provider. + +1. Under **Authorized scope**, check the box by the scope value. +1. Select **Add application**. +1. In the navigation pane on the left, select **API permissions**. It's a best practice to explicitly set the API permissions for the app. + + 1. In the right pane, select **Add a permission**. + 1. Select **Microsoft APIs** then **Microsoft Graph**. + 1. Choose **Delegated permissions** and make sure the permissions you need are selected. This sample requires the permissions listed below. + > [!NOTE] + > Any permission marked as **ADMIN CONSENT REQUIRED** will require both a user and a tenant admin to login. + + - **openid** + - **profile** + - **User.Read** + - **User.ReadBasic.All** + + 1. Select **Add permissions**. + +### Create an OAuth connection setting for SkillBot + +1. Create an Microsoft Entra ID connection in the `SkillBot` bot registration and enter values as described in [Microsoft Entra ID](/azure/bot-service/bot-builder-concept-identity-providers?view=azure-bot-service-4.0&tabs=adv2%2Cga2#azure-ad-v2-1&preserve-view=true) and the values described below. +1. In the **Token Exchange URL** box, enter the `SkillBot` scope value you saved in the previous steps. +1. In the **Scopes** box, enter the following values separated by blank space: `profile` `User.Read` `User.ReadBasic.All` `openid`. + +1. Copy and save to a file the name of the connection. + +## Test the connection + +1. Select on the connection entry to open the connection you created. +1. Select **Test Connection** at the top of the **Service Provider Connection Setting** pane. +1. The first time, this should open a new browser tab listing the permissions your app is requesting and prompt you to accept. +1. Select **Accept**. +1. This should then redirect you to a **Test Connection to \ Succeeded** page. + +For more information, see the [Microsoft Entra ID for developers (v1.0) overview](/azure/active-directory/azuread-dev/v1-overview) and [Microsoft identity platform (v2.0) overview](/azure/active-directory/develop/active-directory-appmodel-v2-overview). +For information about the differences between the v1 and v2 endpoints, see [Why update to Microsoft identity platform (v2.0)?](/azure/active-directory/develop/active-directory-v2-compare). For complete information, see [Microsoft identity platform (formerly Microsoft Entra ID for developers)](/azure/active-directory/develop/). + +## Prepare the samples code + +You must update the `appsettings.json` file in both samples as described below. + +1. Clone the [SSO with Simple Skill Consumer and Skill][cs-auth-sample] sample from the GitHub repository. +1. Open the `SkillBot` project `appsettings.json` file. Assign the following values from the saved file: + + ```json + { + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + "ConnectionName": "", + "AllowedCallers": [ "" ] + } + +1. Open the `RootBot` project `appsettings.json` file. Assign the following values from the saved file: + + ```json + { + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + "ConnectionName": "", + "SkillHostEndpoint": "http://localhost:3978/api/skills/", + "BotFrameworkSkills": [ + { + "Id": "SkillBot", + "AppId": "", + "SkillEndpoint": "http://localhost:39783/api/messages" + } + ] + } + ``` + +## Test the samples + +Use the following for testing: + +- `RootBot` commands + + - `login` allows the user to sign into the Microsoft Entra ID registration using the `RootBot`. Once signed in, SSO takes care of the sign-in into the `SkillBot` also. The user doesn't have to sign in again. + - `token` displays the user's token. + - `logout` logs the user out of the `RootBot`. + +- `SkillBot` commands + + - `skill login` allows the `RootBot` to sign into the `SkillBot`, on behalf of the user. The user isn't shown a sign-in card, if already signed in, unless SSO fails. + - `skill token` displays the user's token from the `SkillBot`. + - `skill logout` logs the user out of the `SkillBot` + +> [!NOTE] +> The first time users try SSO on a skill, they may be presented with an OAuth card to log in. This is because they haven't yet given consent to the skill's Microsoft Entra ID app. To avoid this, they can grant admin consent for any graph permissions requested by the Microsoft Entra ID app. + +### [Emulator](#tab/eml) + +If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). See also [Debug with the Emulator](../bot-service-debug-emulator.md). + +You'll need to configure the Emulator for the bot sample login to work. Use the steps below: +as shown in [Configure the Emulator for authentication](../bot-service-debug-emulator.md#configure-the-emulator-for-authentication). + +After you've configured the authentication mechanism, you can perform the actual bot sample testing. + +1. In Visual Studio, open the `SSOWithSkills.sln` solution and configure it to start [debugging with multiple processes](/visualstudio/debugger/debug-multiple-processes?view=vs-2019#start-debugging-with-multiple-processes&preserve-view=true). +1. Start debugging locally on your machine. +Notice that in the`RootBot` project `appsettings.json` file you've the following settings: + + ```json + "SkillHostEndpoint": "http://localhost:3978/api/skills/" + "SkillEndpoint": "http://localhost:39783/api/messages" + ``` + + > [!NOTE] + > These settings imply that, with both `RootBot` and `SkillBot` are running on the local machine. The Emulator communicates with `RootBot` on port 3978 and `RootBot` communicates with `SkillBot` on port 39783. As soon as you start debugging, two default browser windows open. One on port 3978 and the other on port 39783. + +1. Start the Emulator. +1. When you connect to the bot, enter your `RootBot` registration app ID and password. +1. Type `hi` to start the conversation. +1. Enter **login**. The `RootBot` will display a _Sign In to AAD_ authentication card. + + :::image type="content" source="media/how-to-auth/auth-bot-sso-test-root-signin.PNG" alt-text="Example of a sign-in card."::: + +1. Select **Sign In**. The pop-up dialog _Confirm Open URL_ is displayed. + + :::image type="content" source="media/how-to-auth/auth-bot-test-confirm-url.PNG" alt-text="Screenshot of the 'open URL' confirmation message."::: + +1. Select **Confirm**. You'll be logged in and the `RootBot` token is displayed. +1. Enter **token** to display the token again. + + :::image type="content" source="media/how-to-auth/auth-bot-sso-test-token.PNG" alt-text="Example of a message displaying the root token."::: + + Now you're ready to communicate with the `SkillBot`. Once you've signed using the `RootBot`, you don't need to provide your credentials again until you sign out. This demonstrates that SSO is working. + +1. Enter **skill login** in the Emulator box. You'll not be asked to log in again. Instead the SkillBot token is displayed. +1. Enter **skill token** to display the token again. +1. Now you can enter **skill logout** to sign out of the `SkillBot`. Then enter **logout** to sign out of the `SimpleRootBoot`. + +### [Web Chat](#tab/wct) + +1. Deploy the root bot and the skill bot to Azure. For more information, see [Tutorial: Provision a bot in Azure](../tutorial-provision-a-bot.md) and [Tutorial: Publish a basic bot](../tutorial-publish-a-bot.md). +1. In your code editor, for example Visual Studio, replace the localhost addresses in the `RootBot` project `appsetting.js` file with the actual Microsoft Entra IDdresses as shown below. + + ```json + "SkillHostEndpoint": "https://.azurewebsites.net/api/skills" + "SkillEndpoint": "https://.azurewebsites.net/api/messages" + ```` + +1. In your browser, go to the [Azure portal][azure-portal]. +1. Open your root bot registration. In the left pane, select **Test in Web Chat**. The dialog window with your root bot is displayed with the bot greeting message. +1. Start the conversation with the bot by entering _hi_ for example. The bot will echo your message back. +1. Enter **login**. The `RootBot` will display a _Sign In to AAD_ authentication card. + + :::image type="content" source="media/how-to-auth/auth-bot-sso-test-webchat-root-signin.PNG" alt-text="Example of a sign-in card."::: + +1. Select **Sign In**. A web page with a validation code is displayed. +1. To finish signing in, copy the code and enter it in the input box. The `RootBot` token is displayed. +1. Enter **token** to display the token again. + + :::image type="content" source="media/how-to-auth/auth-bot-sso-test-webchat-token.PNG" alt-text="Example of a message displaying the root token."::: + + Now you're ready to communicate with the `SkillBot`. Once you've signed in the `RootBot`, you don't need to provide your credentials again until you sign out. This demonstrates that SSO is working. + +1. Enter **skill login**. The SkillBot token is displayed. +1. Enter **skill token** to display the token again. This tells you that you're communicating with the `SkillBot` without the need to sign in again. SSO in action! +1. Now you can enter **skill logout** to sign out of the `SkillBot`. Then enter **logout** to sign out of the `SimpleRootBoot`. + +--- + +## Additional information + +The following time-sequence diagram applies to the samples used in the article and shows the interaction between the various components involved. _ABS_ stands for _Azure AI Bot Service_. + +:::image type="content" source="media/how-to-auth/auth-bot-sso-sample-flow-diagram.PNG" alt-text="Sequence diagram illustrating the skill token flow."::: + +1. The first time, the user enters the `login` command for the **RootBot**. +1. The **RootBot** sends an **OAuthCard** asking the user to sign in. +1. The user enters the authentication credentials that are sent to the **ABS** (Azure AI Bot Service). +1. The **ABS** sends the authentication token, generated based on the user's credentials, to the **RootBot**. +1. The **RootBot** displays the root token for the user to see. +1. The user enters the `skill login` command for the **SkillBot**. +1. The **SkillBot** sends an **OAuthCard** to the **RootBot**. +1. The **RootBot** asks for an **exchangeable token** from **ABS**. +1. SSO sends the **SkillBot** **skill token** to the **RootBot**. +1. The **RootBot** displays the skill token for the user to see. Notice that the skill token was generated without the user having to sign in the **SKillBot**. This is because of the SSO. + +The following example shows how the token exchange happens. The code is from the [TokenExchangeSkillHandler.cs](https://github.com/microsoft/BotBuilder-Samples/blob/master/experimental/sso-with-skills/RootBot/TokenExchangeSkillHandler.cs) file. + +[!code-csharp[sso-token-exchange](~/../botbuilder-samples/experimental/sso-with-skills/RootBot/TokenExchangeSkillHandler.cs?range=92-136)] + +[azure-portal]: https://ms.portal.azure.com + +[concept-basics]: bot-builder-basics.md +[concept-state]: bot-builder-concept-state.md +[concept-dialogs]: bot-builder-concept-dialog.md + +[simple-dialog]: bot-builder-dialog-manage-conversation-flow.md +[component-dialogs]: bot-builder-compositcontrol.md + +[cs-auth-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/experimental/sso-with-skills diff --git a/articles/v4sdk/bot-builder-authentication.md b/articles/v4sdk/bot-builder-authentication.md index 261811866..f8b7ff323 100644 --- a/articles/v4sdk/bot-builder-authentication.md +++ b/articles/v4sdk/bot-builder-authentication.md @@ -1,148 +1,114 @@ --- -title: Add authentication to your bot via Azure Bot Service - Bot Service -description: Learn how to use the Azure Bot Service authentication features to add SSO to your bot. -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/7/2020 +title: Add authentication to a bot in Bot Framework SDK +description: Learn how to add user's authentication to your bot using Azure authentication. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen monikerRange: 'azure-bot-service-4.0' --- - +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] - +This article references two samples. One shows how to obtain an authentication token. The other is more complex and shows how to access [Microsoft Graph](/en-us/graph) on behalf of the user. In both cases, you can use Azure AD v1 or v2 as an identity provider to obtain an OAuth token for the bot. +This article covers how to: -# Add authentication to your bot via Azure Bot Service +- Create an Azure Bot resource +- Create the Microsoft Entra ID identity provider +- Register the Microsoft Entra ID identity provider with the bot +- Prepare the bot code -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] +Once you finish this article, you'll have a bot that can respond to a few simple tasks. In the Microsoft Graph example, you can send an email, display who you are, and check recent emails. You don't need to publish the bot to test the OAuth features; however, the bot will need valid Azure app ID and password. -The Azure Bot Service and the v4 SDK include new bot authentication capabilities, providing features to make it easier to develop a bot that authenticates users to various identity providers, such as Azure AD (Azure Active Directory), GitHub, Uber, and so on. These capabilities can improve the user experience by eliminating the _magic code verification_ for some clients. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] -Prior to this, your bot needed to include OAuth controllers and login links, store the target client IDs and secrets, and perform user token management. The bot would ask the user sign in on a website, which would then generate a _magic code_ the user could use to verify their identity. - -Now, bot developers no longer need to host OAuth controllers or manage the token life-cycle, as all of this can now be done by the Azure Bot Service. - -The features include: - -- Improvements to the channels to support new authentication features, such as new WebChat and DirectLineJS libraries to eliminate the need for the 6-digit magic code verification. -- Improvements to the Azure Portal to add, delete, and configure connection settings to various OAuth identity providers. -- Support for a variety of out-of-the-box identity providers including Azure AD (both v1 and v2 endpoints), GitHub, and others. -- Updates to the C# and Node.js Bot Framework SDKs to be able to retrieve tokens, create OAuthCards and handle TokenResponse events. -- Samples for how to make a bot that authenticates to Azure AD. - -For more information about how the Azure Bot Service handles authentication, see [User authentication within a conversation](bot-builder-concept-authentication.md). - -You can extrapolate from the steps in this article to add such features to an existing bot. These sample bots demonstrate the new authentication features. - -> [!NOTE] -> The authentication features also work with BotBuilder v3. However, this article covers just the v4 sample code. - -### About this sample - -You need to create an Azure bot resource, and you need: - -1. An Azure AD app registration to allow your bot to access an external resource, such as Office 365. -1. A separate bot resource. The bot resource registers your bot's credentials, and you need these credentials to test the authentication features, even when running your bot code locally. +## Web Chat and Direct Line considerations > [!IMPORTANT] -> Whenever you register a bot in Azure, it gets assigned an Azure AD app. However, this app secures channel-to-bot access. You need an additional AAD app for each application that you want the bot to be able to authenticate on behalf of the user. +> You need to use Direct Line with enhanced authentication enabled to mitigate security risks when connecting to a bot using the Web Chat control. For more information, see [Direct Line enhanced authentication](bot-builder-security-enhanced.md). -This article describes a sample bot that connects to the Microsoft Graph using an Azure AD v1 or v2 token. It also covers how to create and register the associated Azure AD app. As part of this process, you'll use code from the [Microsoft/BotBuilder-Samples](https://github.com/Microsoft/BotBuilder-Samples) GitHub repo. This article covers these processes. +## Prerequisites -- **Create your bot resource** -- **Create an Azure AD application** -- **Register your Azure AD application with your bot** -- **Prepare the bot sample code** +- Knowledge of [bot basics][concept-basics], [managing state][concept-state], the [dialogs library][concept-dialogs], and +how to [implement sequential conversation flow][simple-dialog], and how to [reuse dialogs][component-dialogs]. +- Knowledge of Azure and OAuth 2.0 development. +- Visual Studio 2017 or later for .NET. +- Node.js for JavaScript. +- Python [3.8+](https://www.python.org/downloads/release/python-383/) for Python. +- One of the samples listed below. -Once you finish, you will have a bot running locally that can respond to a few simple tasks against an Azure AD application, such as checking and sending an email, or displaying who you are and who your manager is. To do this, your bot will use a token from an Azure AD application against the Microsoft.Graph library. You do not need to publish your bot to test the OAuth sign-in features; however, your bot will need a valid Azure app ID and password. + | Sample | BotBuilder version | Demonstrates | + |:---|:---:|:---| + | **Authentication** in [**C#**][cs-auth-sample] or [**JavaScript**][js-auth-sample] or [**Java**][java-auth-sample] or [**Python**][python-auth-sample] | v4 | OAuthCard support | + | **Authentication for Microsoft Graph** in [**C#**][cs-msgraph-sample] or [**JavaScript**][js-msgraph-sample] or [**Java**][java-msgraph-sample] or [**Python**][python-msgraph-sample]| v4 | Microsoft Graph API support with OAuth 2.0 | + | **Authentication for Microsoft Teams** in [**C#**][cs-teams-auth-sample] or [**JavaScript**][js-teams-auth-sample] or [**Java**][java-teams-auth-sample] or [**Python**][python-teams-auth-sample]| v4 | Microsoft Graph API support with OAuth 2.0 | -### Web Chat and Direct Line considerations + To run the samples referenced in this article, you need: - + - An Microsoft Entra ID application with which to register a bot resource in Azure. This application allows the bot to access an external secured resource, such as Microsoft Graph. It also allows the user to communicate with the bot via several channels such as Web Chat. + - A separate Microsoft Entra ID application to function as the identity provider. This application provides the credentials needed to establish an OAuth connection between the bot and the secured resource. Notice that this article uses Active Directory as an identity provider. Many other providers are also supported. > [!IMPORTANT] -> Please, keep in mind these important [Security considerations](../rest-api/bot-framework-rest-direct-line-3-0-authentication.md#security-considerations). +> Whenever you register a bot in Azure, it gets assigned an Microsoft Entra ID application. However, this application secures channel-to-bot access. You need an additional Microsoft Entra ID application for each external secured resource you want the bot to access on behalf of the user. -## Prerequisites - -- Knowledge of [bot basics][concept-basics], [managing state][concept-state], the [dialogs library][concept-dialogs], how to [implement sequential conversation flow][simple-dialog], and how to [reuse dialogs][component-dialogs]. -- Knowledge of Azure and OAuth 2.0 development. -- Visual Studio 2017 or later, Node.js, npm, and git. -- One of these samples. +[!INCLUDE [azure bot resource](../includes/azure-bot-resource/azure-bot-resource.md)] -| Sample | BotBuilder version | Demonstrates | -|:---|:---:|:---| -| **Bot authentication** in [**CSharp**][cs-auth-sample] or [**JavaScript**][js-auth-sample] or [**Python**][python-auth-sample] | v4 | OAuthCard support | -| **Bot authentication MSGraph** in [**CSharp**][cs-msgraph-sample] or [**JavaScript**][js-msgraph-sample] or [**Python**](https://aka.ms/bot-auth-msgraph-python-sample-code)| v4 | Microsoft Graph API support with OAuth 2 | +## Microsoft Entra ID identity service -## Create your bot resource on Azure +The Microsoft Entra ID is a cloud identity service that allows you to build applications that securely sign in users using industry standard protocols like OAuth 2.0. -Create a **Bot resource** using the [Azure Portal](https://portal.azure.com/). +You can use one of these two identity services: -For more information, see [Create a bot with Azure Bot Service](./abs-quickstart.md). +1. Microsoft Entra ID developer platform (v1.0). Also known as the **Azure AD v1** endpoint, which allows you to build apps that securely sign in users with a Microsoft work or school account. For more information, see the [Microsoft Entra ID for developers (v1.0) overview](/azure/active-directory/azuread-dev/v1-overview). +1. Microsoft identity platform (v2.0). Also known as the **Microsoft Entra ID** endpoint, which is an evolution of the Azure AD platform (v1.0). It allows you to build applications that sign in to all Microsoft identity providers and get tokens to call Microsoft APIs, such as Microsoft Graph, or other APIs that developers have built. For more information, see the [Microsoft identity platform (v2.0) overview](/azure/active-directory/develop/active-directory-appmodel-v2-overview). -## Create and register an Azure AD application +For information about the differences between the v1 and v2 endpoints, see [Why update to Microsoft identity platform (v2.0)?](/azure/active-directory/develop/active-directory-v2-compare). For complete information, see [Microsoft identity platform (formerly Microsoft Entra ID for developers)](/azure/active-directory/develop/). -You need an Azure AD application that your bot can use to connect to the Microsoft Graph API. +### Create the Microsoft Entra ID identity provider -For this bot you can use Azure AD v1 or v2 endpoints. -For information about the differences between the v1 and v2 endpoints, see the [v1-v2 comparison](https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-compare) and the [Azure AD v2.0 endpoint overview](https://docs.microsoft.com/azure/active-directory/develop/active-directory-appmodel-v2-overview). - -### Create your Azure AD application - -Use these steps to create a new Azure AD application. You can use the v1 or v2 endpoints with the app that you create. +This section shows how to create an Microsoft Entra ID identity provider that uses OAuth 2.0 to authenticate the bot. You can use Azure AD v1 or Microsoft Entra ID endpoints. > [!TIP] -> You will need to create and register the Azure AD application in a tenant -> in which you can consent to delegate permissions requested by an application. +> You'll need to create and register the Microsoft Entra ID application in a tenant in which you can consent to delegate permissions requested by an application. -1. Open the [Azure Active Directory][azure-aad-blade] panel in the Azure portal. - If you are not in the correct tenant, click **Switch directory** to switch to the correct tenant. (For instruction on creating a tenant, see [Access the portal and create a tenant](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-access-create-new-tenant).) +1. Open the [Microsoft Entra ID][azure-aad-blade] panel in the Azure portal. + If you aren't in the correct tenant, select **Switch directory** to switch to the correct tenant. (For information on how to create a tenant, see [Access the portal and create a tenant](/azure/active-directory/fundamentals/active-directory-access-create-new-tenant).) 1. Open the **App registrations** panel. -1. In the **App registrations** panel, click **New registration**. +1. In the **App registrations** panel, select **New registration**. 1. Fill in the required fields and create the app registration. 1. Name your application. 1. Select the **Supported account types** for your application. (Any of these options will work with this sample.) - 1. For the **Redirect URI** - 1. Select **Web**. - 1. Set the URL to `https://token.botframework.com/.auth/web/redirect`. - 1. Click **Register**. + 1. For the **Redirect URI**, select **Web** and set the URL to one of the [supported OAuth redirect URLs](../ref-oauth-redirect-urls.md). + + 1. Select **Register**. - - Once it is created, Azure displays the **Overview** page for the app. - - Record the **Application (client) ID** value. You will use this value later as the _Client id_ when you register your Azure AD application with your bot. - - Also record the **Directory (tenant) ID** value. You will also use this to register this application with your bot. + - Once it's created, Azure displays the **Overview** page for the app. + - Record the **Application (client) ID** value. You'll use this value later as the _client ID_ when you create the connection string and register the Microsoft Entra ID provider with the bot registration. + - Record the **Directory (tenant) ID** value. You'll use this value to register this provider application with your bot. -1. In the navigation pane, click **Certificates & secrets** to create a secret for your application. +1. In the navigation pane, select **Certificates & secrets** to create a secret for your application. - 1. Under **Client secrets**, click **New client secret**. + 1. Under **Client secrets**, select **New client secret**. 1. Add a description to identify this secret from others you might need to create for this app, such as `bot login`. - 1. Set **Expires** to **Never**. - 1. Click **Add**. - 1. Before leaving this page, record the secret. You will use this value later as the _Client secret_ when you register your Azure AD application with your bot. + 1. For **Expires**, choose a length of time after which the secret will expire. + 1. Select **Add**. + 1. Before leaving **Certificates & secrets**, record the secret. You'll use this value later as the _client secret_ when you register your Microsoft Entra ID application with your bot. -1. In the navigation pane, click **API permissions** to open the **API permissions** panel. It is a best practice to explicitly set the API permissions for the app. +1. In the navigation pane, select **API permissions** to open the **API permissions** panel. It's a best practice to explicitly set the API permissions for the app. - 1. Click **Add a permission** to show the **Request API permissions** pane. + 1. Select **Add a permission** to show the **Request API permissions** pane. 1. For this sample, select **Microsoft APIs** and **Microsoft Graph**. 1. Choose **Delegated permissions** and make sure the permissions you need are selected. This sample requires theses permissions. @@ -156,104 +122,147 @@ Use these steps to create a new Azure AD application. You can use the v1 or v2 e - **User.Read** - **User.ReadBasic.All** - 1. Click **Add permissions**. (The first time a user accesses this app through the bot, they will need to grant consent.) + 1. Select **Add permissions**. (The first time a user accesses this app through the bot, they'll need to grant consent.) -You now have an Azure AD application configured. +You now have an Microsoft Entra ID application configured. -### Register your Azure AD application with your bot +> [!NOTE] +> You'll assign the **Application (client) ID** and the **Client secret**, when you create the connection string and register the identity provider with the bot registration. See next section. + +### Register the Microsoft Entra ID identity provider with the bot -The next step is to register with your bot the Azure AD application that you just created. +The next step is to register your identity provider with your bot. -#### Azure AD v1 +#### [Microsoft Entra ID](#tab/aadv2) -1. Navigate to your bot's resource page on the [Azure Portal](https://portal.azure.com/). -1. Click **Settings**. -1. Under **OAuth Connection Settings** near the bottom of the page, click **Add Setting**. +1. Open your bot's Azure Bot resource page in the [Azure portal][azure-portal]. +1. Select **Settings**. +1. Under **OAuth Connection Settings** near the bottom of the page, select **Add Setting**. 1. Fill in the form as follows: - 1. For **Name**, enter a name for your connection. You'll use this name in your bot code. - 1. For **Service Provider**, select **Azure Active Directory**. Once you select this, the Azure AD-specific fields will be displayed. - 1. For **Client id**, enter the application (client) ID that you recorded for your Azure AD v1 application. - 1. For **Client secret**, enter the secret that you created to grant the bot access to the Azure AD app. - 1. For **Grant Type**, enter `authorization_code`. - 1. For **Login URL**, enter `https://login.microsoftonline.com`. - 1.For **Tenant ID**, enter the **directory (tenant) ID** that your recorded earlier for your AAD app or **common** depending on the supported account types selected when you created the ADD app. To decide which value to assign follow these criteria: + 1. **Name**. Enter a name for your connection. You'll use it in your bot code. + 1. **Service Provider**. Select **Microsoft Entra ID** to display Microsoft Entra ID-specific fields. + 1. **Client id**. Enter the application (client) ID you recorded for your Microsoft Entra ID identity provider. + 1. **Client secret**. Enter the secret you recorded for your Microsoft Entra ID identity provider. + + > [!TIP] + > If you want to use certificates, you can select the **AAD v2 with Certificates** provider. + > You'll need to give Bot Service Token Store (appid: 00001111-aaaa-2222-bbbb-3333cccc4444) the permission to get the certificate. - - When creating the AAD app if you selected either *Accounts in this organizational directory only (Microsoft only - Single tenant)* or *Accounts in any organizational directory(Microsoft AAD directory - Multi tenant)* enter the **tenant ID** you recorded earlier for the AAD app. + 1. **Token Exchange URL**. Leave it blank because it's used for SSO in Microsoft Entra ID only. + 1. **Tenant ID**. Enter the **directory (tenant) ID** that you recorded earlier for your Microsoft Entra ID app or **common** depending on the supported account types selected when you created the Azure DD app. To decide which value to assign, follow these criteria: - - However, if you selected *Accounts in any organizational directory (Any AAD directory - Multi tenant and personal Microsoft accounts e.g. Skype, Xbox, Outlook.com)* enter the word **common** instead of a tenant ID. Otherwise, the AAD app will verify through the tenant whose ID was selected and exclude personal MS accounts. + - When creating the Microsoft Entra ID app, if you selected **Accounts in this organizational directory only (Microsoft only - Single tenant)**, enter the tenant ID you recorded earlier for the Microsoft Entra ID app. + - However, if you selected **Accounts in any organizational directory (Any Microsoft Entra ID directory - Multi tenant and personal Microsoft accounts e.g. Xbox, Outlook.com)** or **Accounts in any organizational directory(Microsoft Entra ID directory - Multi tenant)**, enter `common` instead of a tenant ID. Otherwise, the Microsoft Entra ID app will verify through the tenant whose ID was selected and exclude personal Microsoft accounts. - This will be the tenant associated with the users who can be authenticated. + This will be the tenant associated with the users who can be authenticated. For more information, see [Tenancy in Microsoft Entra ID](/azure/active-directory/develop/single-and-multi-tenant-apps). - 1. For **Resource URL**, enter `https://graph.microsoft.com/`. - 1. Leave **Scopes** blank. + 1. For **Scopes**, enter the names of the permission you chose from the application registration. For testing purposes, you can just enter: + `openid profile`. -1. Click **Save**. + > [!NOTE] + > For Microsoft Entra ID, **Scopes** field takes a case-sensitive, space-separated list of values. -> [!NOTE] -> These values enable your application to access Office 365 data via the Microsoft Graph API. +1. Select **Save**. -#### Azure AD v2 +#### [Azure AD v1](#tab/aadv1) -1. Navigate to your bot's Bot Channels Registration page on the [Azure Portal](https://portal.azure.com/). -1. Click **Settings**. -1. Under **OAuth Connection Settings** near the bottom of the page, click **Add Setting**. +1. Navigate to your bot's resource page on the [Azure portal][azure-portal]. +1. Select **Settings**. +1. Under **OAuth Connection Settings** near the bottom of the page, select **Add Setting**. 1. Fill in the form as follows: - 1. For **Name**, enter a name for your connection. You'll use it in your bot code. - 1. For **Service Provider**, select **Azure Active Directory v2**. Once you select this, the Azure AD-specific fields will be displayed. + 1. For **Name**, enter a name for your connection. You'll use this name in your bot code. + 1. For **Service Provider**, select **Microsoft Entra ID**. Once you select this, the Microsoft Entra ID-specific fields will be displayed. 1. For **Client id**, enter the application (client) ID that you recorded for your Azure AD v1 application. - 1. For **Client secret**, enter the secret that you created to grant the bot access to the Azure AD app. - 1. For **Tenant ID**, enter the **directory (tenant) ID** that your recorded earlier for your AAD app or **common** depending on the supported account types selected when you created the ADD app. To decide which value to assign follow these criteria: + 1. For **Client secret**, enter the secret that you created to grant the bot access to the Microsoft Entra ID app. + 1. For **Grant Type**, enter `authorization_code`. + 1. For **Login URL**, enter `https://login.microsoftonline.com`. + 1. For **Tenant ID**, enter the **directory (tenant) ID** that you recorded earlier for your Microsoft Entra ID app or **common** depending on the supported account types selected when you created the ADD app. To decide which value to assign, follow these criteria: - - When creating the AAD app if you selected either *Accounts in this organizational directory only (Microsoft only - Single tenant)* or *Accounts in any organizational directory(Microsoft AAD directory - Multi tenant)* enter the **tenant ID** you recorded earlier for the AAD app. + - When creating the Microsoft Entra ID app, if you selected **Accounts in this organizational directory only (Microsoft only - Single tenant)**, enter the tenant ID you recorded earlier for the Microsoft Entra ID app. + - However, if you selected **Accounts in any organizational directory (Any Microsoft Entra ID directory - Multi tenant and personal Microsoft accounts e.g. Xbox, Outlook.com)** or **Accounts in any organizational directory(Microsoft Entra ID directory - Multi tenant)**, enter `common` instead of a tenant ID. Otherwise, the Microsoft Entra ID app will verify through the tenant whose ID was selected and exclude personal MS accounts. - - However, if you selected *Accounts in any organizational directory (Any AAD directory - Multi tenant and personal Microsoft accounts e.g. Skype, Xbox, Outlook.com)* enter the word **common** instead of a tenant ID. Otherwise, the AAD app will verify through the tenant whose ID was selected and exclude personal MS accounts. + This will be the tenant associated with the users who can be authenticated. For more information, see [Tenancy in Microsoft Entra ID](/azure/active-directory/develop/single-and-multi-tenant-apps). - This will be the tenant associated with the users who can be authenticated. + 1. For **Resource URL**, enter `https://graph.microsoft.com/`. + 1. Leave **Scopes** blank. - 1. For **Scopes**, enter the names of the permission you chose from application registration: - `Mail.Read Mail.Send openid profile User.Read User.ReadBasic.All`. +1. Select **Save**. - > [!NOTE] - > For Azure AD v2, **Scopes** takes a case-sensitive, space-separated list of values. - -1. Click **Save**. +--- > [!NOTE] > These values enable your application to access Office 365 data via the Microsoft Graph API. +> Also, the **Token Exchange URL** should be left blank because it's used for SSO in Microsoft Entra ID only. ### Test your connection -1. Click on the connection entry to open the connection you just created. -1. Click **Test Connection** at the top of the **Service Provider Connection Setting** pane. +1. Select on the connection entry to open the connection you created. +1. Select **Test Connection** at the top of the **Service Provider Connection Setting** pane. 1. The first time, this should open a new browser tab listing the permissions your app is requesting and prompt you to accept. -1. Click **Accept**. +1. Select **Accept**. 1. This should then redirect you to a **Test Connection to \ Succeeded** page. You can now use this connection name in your bot code to retrieve user tokens. ## Prepare the bot code -You will need your bot's app ID and password to complete this process. - -# [C#](#tab/csharp) +You'll need your bot's app ID and password to complete this process. - +### [C#](#tab/csharp) -1. Clone from the github repository the sample you want to work with: [**Bot authentication**][cs-auth-sample] or [**Bot authentication MSGraph**][cs-msgraph-sample]. +1. Clone from the GitHub repository the sample you want to work with: [**Bot authentication**][cs-auth-sample] or [**Bot authentication for Microsoft Graph**][cs-msgraph-sample]. 1. Update **appsettings.json**: - Set `ConnectionName` to the name of the OAuth connection setting you added to your bot. - Set `MicrosoftAppId` and `MicrosoftAppPassword` to your bot's app ID and app secret. Depending on the characters in your bot secret, you may need to XML escape the password. For example, any ampersands (&) will need to be encoded as `&`. - + [!code-json[appsettings](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/appsettings.json)] - -# [JavaScript](#tab/javascript) - -1. Clone from the github repository you want to work with: [**Bot authentication**][js-auth-sample] or [**Bot authentication MSGraph**][js-msgraph-sample]. + + To use OAuth in bot with data-residency in public cloud, you must add the following configurations in your appsettings + ```json + "OAuthUrl": "", + "ToChannelFromBotOAuthScope": "https://api.botframework.com", + "ToChannelFromBotLoginUrlTemplate": "https://api.botframework.com", + "PublicAzureChannel": "https://api.botframework.com", + "ToBotFromChannelOpenIdMetadataUrl": "https://login.botframework.com/v1/.well-known/openidconfiguration", + "ToBotFromEmulatorOpenIdMetadataUrl": "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", + "ToBotFromChannelTokenIssuer": "https://api.botframework.com", + "ToChannelFromBotLoginUrl": "https://login.microsoftonline.com/botframework.com", + ``` + + Where _\_ is one of the following URIs: + + |URI|Description| + |:-|:-| + |`https://europe.api.botframework.com`|For public-cloud bots with data residency in Europe.| + |`https://unitedstates.api.botframework.com`|For public-cloud bots with data residency in the United States.| + |`https://india.api.botframework.com`|For public-cloud bots with data residency in India.| + +1. Update **Startup.cs**: + + To use OAuth in _non-public Azure clouds_, like the government cloud, you must add the following code in the **Startup.cs** file. + + ```csharp + string uri = ""; + MicrosoftAppCredentials.TrustServiceUrl(uri); + OAuthClientConfig.OAuthEndpoint = uri; + + ``` + + Where _\_ is one of the following URIs: + + |URI|Description| + |:-|:-| + |`https://api.botframework.azure.us`|For United States government-cloud bots without data residency.| + |`https://api.botframework.com`|For public-cloud bots without data residency. This is the default URI and doesn't require a change to **Startup.cs**.| + +### [JavaScript](#tab/javascript) + +1. Clone from the GitHub repository you want to work with: [**Bot authentication**][js-auth-sample] or [**Bot authentication for Microsoft Graph**][js-msgraph-sample]. 1. Update **.env**: - Set `connectionName` to the name of the OAuth connection setting you added to your bot. @@ -261,11 +270,21 @@ You will need your bot's app ID and password to complete this process. Depending on the characters in your bot secret, you may need to XML escape the password. For example, any ampersands (&) will need to be encoded as `&`. - [!code-txt[.env](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/.env)] + [!code-ini[.env](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/.env)] + +### [Java](#tab/java) + +1. Clone from the GitHub repository the sample you want to work with: [**Bot authentication**][java-auth-sample] or [**Bot authentication for Microsoft Graph**][java-msgraph-sample]. +1. Update **application.properties**: + + - Set `ConnectionName` to the name of the OAuth connection setting you added to your bot. + - Set `MicrosoftAppId` and `MicrosoftAppPassword` to your bot's app ID and app secret. -# [Python](#tab/python) + [!code-ini[application.properties](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/resources/application.properties)] -1. Clone the sample [**Bot authentication**][python-auth-sample] from the github repository. +### [Python](#tab/python) + +1. Clone the GitHub repository the sample you want to work with: [**Bot authentication**][python-auth-sample] or [**Bot authentication for Microsoft Graph**][python-msgraph-sample]. 1. Update **config.py**: - Set `ConnectionName` to the name of the OAuth connection setting you added to your bot. @@ -277,46 +296,48 @@ You will need your bot's app ID and password to complete this process. --- -If you do not know how to get your **Microsoft app ID** and **Microsoft app password** values, you can create a new password [as described here](../bot-service-quickstart-registration.md#get-registration-password) +To obtain the **Microsoft app ID** and **Microsoft app password** values, see [Get registration password](~/bot-service-manage-settings.md#bot-identity-information). > [!NOTE] -> You could now publish this bot code to your Azure subscription (right-click on the project and choose **Publish**), but it is not necessary for this article. You would need to set up a publishing configuration that uses the application and hosting plan that you used when configuration the bot in the Azure Portal. +> You could now publish this bot code to your Azure subscription (right-select on the project and choose **Publish**), but it's not necessary for this article. You would need to set up a publishing configuration that uses the application and hosting plan that you used when configuration the bot in the Azure portal. -## Test the bot using the emulator +## Test the bot using the Emulator -If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). See also [Debug with the emulator](../bot-service-debug-emulator.md). +If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). See also [Debug with the Emulator](../bot-service-debug-emulator.md). - -In order for the bot sample login to work you must configure the emulator -as shown in [Configure the emulator for authentication](../bot-service-debug-emulator.md#configure-the-emulator-for-authentication). +In order for the bot sample login to work you must configure the Emulator +as shown in [Configure the Emulator for authentication](../bot-service-debug-emulator.md#configure-the-emulator-for-authentication). ### Testing -After you have configured the authentication mechanism, you can perform the actual bot sample testing. +After you've configured the authentication mechanism, you can perform the actual bot sample testing. + +> [!NOTE] +> You may be asked to enter a _magic code_, because the way the bot sample is implemented. This magic code is part of the [RFC#7636](https://tools.ietf.org/html/rfc7636#page-5) and is there to add an extra security element. By removing the magic code, there's an increased security risk. This can be mitigated using Direct Line with enhanced authentication enabled. For more information, see [Bot Framework enhanced authentication](bot-builder-security-enhanced.md). 1. Run the bot sample locally on your machine. -1. Start the emulator. -1. You will need to provide your bot's app ID and password when you connect to the bot. - - You get the app ID and the password from the Azure app registration. These are the same values you assigned to the bot app in the `appsettings.json` or `.env` file. In the emulator, you assign these values in the configuration file or the first time you connect to the bot. +1. Start the Emulator. +1. You'll need to provide your bot's app ID and password when you connect to the bot. + - You get the app ID and the password from the Azure app registration. These are the same values you assigned to the bot app in the `appsettings.json` or `.env` file. In the Emulator, you assign these values in the configuration file or the first time you connect to the bot. - If you needed to XML-escape the password in your bot code, you also need to do so here. 1. Type `help` to see a list of available commands for the bot, and test the authentication features. 1. Once you've signed in, you don't need to provide your credentials again until you sign out. 1. To sign out, and cancel your authentication, type `logout`. > [!NOTE] -> Bot authentication requires use of the Bot Connector Service. The service accesses the bot channels registration information for your bot. +> Bot authentication requires use of the Bot Connector service. The service accesses information from your Azure Bot resource. -## Bot authentication example +## Authentication example In the **Bot authentication** sample, the dialog is designed to retrieve the user token after the user is logged in. -![Sample output](media/how-to-auth/auth-bot-test.png) +:::image type="content" source="media/how-to-auth/auth-bot-test.png" alt-text="Sample conversation with the authentication sample bot."::: -## Bot authentication MSGraph example +## Authentication for Microsoft Graph example -In the **Bot authentication MSGraph** sample, the dialog is designed to accept a limited set of commands after the user is logged in. +In the **Bot authentication for Microsoft Graph** sample, the dialog is designed to accept a limited set of commands after the user is logged in. -![Sample output](media/how-to-auth/msgraph-bot-test.png) +:::image type="content" source="media/how-to-auth/msgraph-bot-test.png" alt-text="Sample conversation with the Microsoft Graph authentication sample bot."::: --- @@ -324,20 +345,18 @@ In the **Bot authentication MSGraph** sample, the dialog is designed to accept a When a user asks the bot to do something that requires the bot to have the user logged in, the bot can use an `OAuthPrompt` to initiate retrieving a token for a given connection. The `OAuthPrompt` creates a token retrieval flow that consists of: -1. Checking to see if the Azure Bot Service already has a token for the current user and connection. If there is a token, the token is returned. -1. If Azure Bot Service does not have a cached token, an `OAuthCard` is created which is a sign in button the user can click on. -1. After the user clicks on the `OAuthCard` sign in button, Azure Bot Service will either send the bot the user's token directly or will present the user with a 6-digit authentication code to enter in the chat window. +1. Checking to see if the Azure AI Bot Service already has a token for the current user and connection. If there's a token, the token is returned. +1. If Azure AI Bot Service doesn't have a cached token, an `OAuthCard` is created which is a sign-in button the user can select on. +1. After the user selects on the `OAuthCard` sign-in button, Azure AI Bot Service will either send the bot the user's token directly or present the user with a 6-digit authentication code to enter in the chat window. 1. If the user is presented with an authentication code, the bot then exchanges this authentication code for the user's token. The following sections describe how the sample implements some common authentication tasks. ### Use an OAuth prompt to sign the user in and get a token -# [C#](#tab/csharp) +### [C#](#tab/csharp) -![Bot architecture](media/how-to-auth/architecture.png) - - +:::image type="content" source="media/how-to-auth/architecture.png" alt-text="Architecture diagram for the C# sample."::: **Dialogs\MainDialog.cs** @@ -348,17 +367,17 @@ Add an OAuth prompt to **MainDialog** in its constructor. Here, the value for th Within a dialog step, use `BeginDialogAsync` to start the OAuth prompt, which asks the user to sign in. - If the user is already signed in, this will generate a token response event, without prompting the user. -- Otherwise, this will prompt the user to sign in. The Azure Bot Service sends the token response event after the user attempts to sign in. +- Otherwise, this will prompt the user to sign in. The Azure AI Bot Service sends the token response event after the user attempts to sign in. [!code-csharp[Use the OAuthPrompt](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/Dialogs/MainDialog.cs?range=49)] -Within the following dialog step, check for the presence of a token in the result from the previous step. If it is not null, the user successfully signed in. +Within the following dialog step, check for the presence of a token in the result from the previous step. If it's not null, the user successfully signed in. [!code-csharp[Get the OAuthPrompt result](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/Dialogs/MainDialog.cs?range=54-56)] -# [JavaScript](#tab/javascript) +### [JavaScript](#tab/javascript) -![Bot architecture](media/how-to-auth/architecture-js.png) +:::image type="content" source="media/how-to-auth/architecture-js.png" alt-text="Architecture diagram for the JavaScript sample."::: **dialogs/mainDialog.js** @@ -369,17 +388,38 @@ Add an OAuth prompt to **MainDialog** in its constructor. Here, the value for th Within a dialog step, use `beginDialog` to start the OAuth prompt, which asks the user to sign in. - If the user is already signed in, this will generate a token response event, without prompting the user. -- Otherwise, this will prompt the user to sign in. The Azure Bot Service sends the token response event after the user attempts to sign in. +- Otherwise, this will prompt the user to sign in. The Azure AI Bot Service sends the token response event after the user attempts to sign in. [!code-javascript[Use OAuthPrompt](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/dialogs/mainDialog.js?range=57)] -Within the following dialog step, check for the presence of a token in the result from the previous step. If it is not null, the user successfully signed in. +Within the following dialog step, check for the presence of a token in the result from the previous step. If it's not null, the user successfully signed in. [!code-javascript[Get OAuthPrompt result](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/dialogs/mainDialog.js?range=62-63)] -# [Python](#tab/python) +### [Java](#tab/java) + +:::image type="content" source="media/how-to-auth/architecture-java.png" alt-text="Architecture diagram for the Java sample."::: + +**MainDialog.java** + +Add an OAuth prompt to **MainDialog** in its constructor. Here, the value for the connection name was retrieved from the **application.properties** file. + +[!code-java[Add OAuthPrompt](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/java/com/microsoft/bot/sample/authentication/MainDialog.java?range=26-32)] + +Within a dialog step, use `beginDialog` to start the OAuth prompt, which asks the user to sign in. + +- If the user is already signed in, this will generate a token response event, without prompting the user. +- Otherwise, this will prompt the user to sign in. The Azure AI Bot Service sends the token response event after the user attempts to sign in. -![Bot architecture](media/how-to-auth/architecture-python.png) +[!code-java[Use the OAuthPrompt](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/java/com/microsoft/bot/sample/authentication/MainDialog.java?range=86)] + +Within the following dialog step, check for the presence of a token in the result from the previous step. If it's not null, the user successfully signed in. + +[!code-java[Get the OAuthPrompt result](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/java/com/microsoft/bot/sample/authentication/MainDialog.java?range=54-56)] + +### [Python](#tab/python) + +:::image type="content" source="media/how-to-auth/architecture-python.png" alt-text="Architecture diagram for the Python sample."::: **dialogs/main_dialog.py** @@ -390,11 +430,11 @@ Add an OAuth prompt to **MainDialog** in its constructor. Here, the value for th Within a dialog step, use `begin_dialog` to start the OAuth prompt, which asks the user to sign in. - If the user is already signed in, this will generate a token response event, without prompting the user. -- Otherwise, this will prompt the user to sign in. The Azure Bot Service sends the token response event after the user attempts to sign in. +- Otherwise, this will prompt the user to sign in. The Azure AI Bot Service sends the token response event after the user attempts to sign in. [!code-python[Add OAuthPrompt](~/../botbuilder-samples/samples/python/18.bot-authentication/dialogs/main_dialog.py?range=49)] -Within the following dialog step, check for the presence of a token in the result from the previous step. If it is not null, the user successfully signed in. +Within the following dialog step, check for the presence of a token in the result from the previous step. If it's not null, the user successfully signed in. [!code-python[Add OAuthPrompt](~/../botbuilder-samples/samples/python/18.bot-authentication/dialogs/main_dialog.py?range=54-65)] @@ -404,7 +444,7 @@ Within the following dialog step, check for the presence of a token in the resul When you start an OAuth prompt, it waits for a token response event, from which it will retrieve the user's token. -# [C#](#tab/csharp) +### [C#](#tab/csharp) **Bots\AuthBot.cs** @@ -412,7 +452,7 @@ When you start an OAuth prompt, it waits for a token response event, from which [!code-csharp[OnTokenResponseEventAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/Bots/AuthBot.cs?range=32-38)] -# [JavaScript](#tab/javascript) +### [JavaScript](#tab/javascript) **bots/authBot.js** @@ -420,7 +460,15 @@ When you start an OAuth prompt, it waits for a token response event, from which [!code-javascript[onTokenResponseEvent](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/bots/authBot.js?range=29-31)] -# [Python](#tab/python) +### [Java](#tab/java) + +**AuthBot.java** + +**AuthBot** derives from `Dialog` and explicitly handles token response event activities. Here, we continue the active dialog, which allows the OAuth prompt to process the event and retrieve the token. + +[!code-java[OnTokenResponseEvent](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/java/com/microsoft/bot/sample/authentication/AuthBot.java?range=44-50)] + +### [Python](#tab/python) **bots/auth_bot.py** @@ -432,97 +480,66 @@ When you start an OAuth prompt, it waits for a token response event, from which ### Log the user out -It is best practice to let users explicitly sign out or logout, instead of relying on the connection to time out. +It's best practice to let users explicitly sign out, instead of relying on the connection to time out. -# [C#](#tab/csharp) +### [C#](#tab/csharp) **Dialogs\LogoutDialog.cs** -[!code-csharp[Allow logout](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/Dialogs/LogoutDialog.cs?range=44-61&highlight=11)] -# [JavaScript](#tab/javascript) +[!code-csharp[Allow sign out](~/../botbuilder-samples/samples/csharp_dotnetcore/18.bot-authentication/Dialogs/LogoutDialog.cs?range=45-63&highlight=11)] -**dialogs/logoutDialog.js** -[!code-javascript[Allow logout](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/dialogs/logoutDialog.js?range=31-42&highlight=7)] +### [JavaScript](#tab/javascript) -# [Python](#tab/python) - -**dialogs/logout_dialog.py** -[!code-python[allow logout](~/../botbuilder-samples/samples/python/18.bot-authentication/dialogs/logout_dialog.py?range=27-34&highlight=6)] - ---- - -### Adding Teams Authentication - -Teams behaves somewhat differently than other channels in regards to OAuth and requires a few changes to properly implement authentication. We will add code from the Teams Authentication Bot sample ([C#][cs-teams-auth-sample]/[JavaScript][js-teams-auth-sample]). - -One difference between other channels and Teams is that Teams sends an *invoke* activity to the bot, rather than an *event* activity. - -# [C#](#tab/csharp) - -**Bots/TeamsBot.cs** -[!code-csharp[Invoke Activity](~/../botbuilder-samples/samples/csharp_dotnetcore/46.teams-auth/Bots/TeamsBot.cs?range=34-42&highlight=1)] - -# [JavaScript](#tab/javascript) - -**bots/teamsBot.js** -[!code-javascript[Invoke Activity](~/../botbuilder-samples/samples/javascript_nodejs/46.teams-auth/bots/teamsBot.js?range=16-25&highlight=1)] - -# [Python](#tab/python) - -Microsoft Teams currently differs slightly in the way auth is integrated with the bot. Please, refer to [Teams documentation](https://aka.ms/teams-docs) on authentication. - ---- - -If you use an *OAuth prompt*, this invoke activity must be forwarded to the dialog. We will do so in the `TeamsActivityHandler`. Add the following code to your main dialog file. +**dialogs/logoutDialog.js** -# [C#](#tab/csharp) +[!code-javascript[Allow sign out](~/../botbuilder-samples/samples/javascript_nodejs/18.bot-authentication/dialogs/logoutDialog.js?range=31-43&highlight=8)] -**Bots/DialogBot.cs** -[!code-csharp[Dialogs Handler](~/../botbuilder-samples/samples/csharp_dotnetcore/46.teams-auth/Bots/DialogBot.cs?range=19)] +### [Java](#tab/java) -# [JavaScript](#tab/javascript) +**LogoutDialog.java** -**Bots/dialogBot.js** -[!code-javascript[Dialogs Handler](~/../botbuilder-samples/samples/javascript_nodejs/46.teams-auth/bots/dialogBot.js?range=6)] +[!code-java[Allow sign out](~/../botbuilder-samples/samples/java_springboot/18.bot-authentication/src/main/java/com/microsoft/bot/sample/authentication/LogoutDialog.java?range=49-67&highlight=8-13)] +### [Python](#tab/python) -# [Python](#tab/python) +**dialogs/logout_dialog.py** -Microsoft Teams currently differs slightly in the way auth is integrated with the bot. Please, refer to [Teams documentation](https://aka.ms/teams-docs) on authentication. +[!code-python[allow sign out](~/../botbuilder-samples/samples/python/18.bot-authentication/dialogs/logout_dialog.py?range=27-34&highlight=6)] --- -Finally, make sure to add an appropriate `TeamsActivityHandler` file (`TeamsActivityHandler.cs` for C# bots and `teamsActivityHandler.js` for Javascript bots) at the topmost level in your bot's folder. +### Adding Teams Authentication -The `TeamsActivityHandler` also sends *message reaction* activities. A message reaction activity references the original activity using the *reply to ID* field. This activity should also be visible through the [Activity Feed][teams-activity-feed] in Microsoft Teams. +OAuth is handled differently in Teams than in other channels. The Teams Authentication Bot sample (in [C#][cs-teams-auth-sample], [JavaScript][js-teams-auth-sample], [Java][java-teams-auth-sample], or [Python][python-teams-auth-sample]) demonstrates how to properly implement authentication for Teams. -> [!NOTE] -> You need to create a manifest and include `token.botframework.com` in the `validDomains` section; otherwise the OAuthCard **Sign in** button will not open the authentication window. Use the [App Studio](https://docs.microsoft.com/microsoftteams/platform/get-started/get-started-app-studio) to generate your manifest. ### Further reading -- [Bot Framework additional resources](https://docs.microsoft.com/azure/bot-service/bot-service-resources-links-help) includes links for additional support. +- [Bot Framework additional resources](../bot-service-resources-links-help.md) includes links for additional support. - The [Bot Framework SDK](https://github.com/microsoft/botbuilder) repo has more information about repos, samples, tools, and specs associated with the Bot Builder SDK. - - -[Azure portal]: https://ms.portal.azure.com +[azure-portal]: https://ms.portal.azure.com [azure-aad-blade]: https://ms.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview -[aad-registration-blade]: https://ms.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredAppsPreview [concept-basics]: bot-builder-basics.md [concept-state]: bot-builder-concept-state.md [concept-dialogs]: bot-builder-concept-dialog.md [simple-dialog]: bot-builder-dialog-manage-conversation-flow.md -[dialog-prompts]: bot-builder-prompts.md [component-dialogs]: bot-builder-compositcontrol.md -[cs-auth-sample]: https://aka.ms/v4cs-bot-auth-sample -[js-auth-sample]: https://aka.ms/v4js-bot-auth-sample -[python-auth-sample]: https://aka.ms/bot-auth-python-sample-code +[cs-auth-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/18.bot-authentication +[js-auth-sample]: https://github.com/Microsoft/BotBuilder-Samples/blob/main/samples/javascript_nodejs/18.bot-authentication +[java-auth-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/18.bot-authentication +[python-auth-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/18.bot-authentication + +[cs-msgraph-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/24.bot-authentication-msgraph +[js-msgraph-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/24.bot-authentication-msgraph +[java-msgraph-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/24.bot-authentication-msgraph +[python-msgraph-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/24.bot-authentication-msgraph + +[cs-teams-auth-sample]:https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/csharp +[js-teams-auth-sample]:https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation-sso-quickstart/js +[java-teams-auth-sample]:https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/java +[python-teams-auth-sample]:https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-teams-authentication/python -[cs-msgraph-sample]: https://aka.ms/v4cs-auth-msgraph-sample -[js-msgraph-sample]: https://aka.ms/v4js-auth-msgraph-sample -[cs-teams-auth-sample]:https://aka.ms/cs-teams-auth-sample -[js-teams-auth-sample]:https://aka.ms/js-teams-auth-sample -[teams-activity-feed]:[https://aka.ms/teams-activity-feed +[teams-activity-feed]:/microsoftteams/platform/concepts/activity-feed diff --git a/articles/v4sdk/bot-builder-basics-teams.md b/articles/v4sdk/bot-builder-basics-teams.md index 9bc0196c7..a5f8c7aa0 100644 --- a/articles/v4sdk/bot-builder-basics-teams.md +++ b/articles/v4sdk/bot-builder-basics-teams.md @@ -1,135 +1,306 @@ --- -title: How bots for Microsoft Teams work -description: A continuation of the article on How bots work specific to Microsoft Teams bots -author: WashingtonKayaker -ms.author: kamrani -manager: kamrani +title: Build Microsoft Teams bots with Bot Framework SDK +description: A continuation of the article on How bots work, specific to Microsoft Teams bots. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service ms.topic: overview -ms.service: bot-service -ms.date: 10/31/2019 -monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # How Microsoft Teams bots work -This is an introduction that builds on what you learned in the article [How bots work](https://docs.microsoft.com/azure/bot-service/bot-builder-basics), you should be familiar with that article before reading this. +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -The primary differences in bots developed for Microsoft Teams is in how activities are handled. The Microsoft Teams activity handler derives from the Bot Framework's activity handler to route all teams activities before allowing any non-teams specific activities to be handled. +This article builds on what you learned in [How bots work](bot-builder-basics.md) and [Event-driven conversations](bot-activity-handler-concept.md); you should be familiar with these articles before you continue. -## Teams Activity handlers +The primary difference in bots developed for Microsoft Teams is in how activities are handled. The _Teams activity handler_ derives from the _activity handler_ and processes Teams-specific activity types before processing more general activity types. -Just like any other bot, when a bot is designed for Microsoft Teams receives an activity, it passes it on to its *activity handlers*. Under the covers, there is one base handler called the *turn handler*, that all activities are routed through. The *turn handler* calls the required activity handler to handle whatever type of activity was received. Where a bot designed for Microsoft Teams differs is that it is derived from a _Teams_ `Activity Handler` class that is derived from the Bot Framework's `Activity Handler` class. The _Teams_ `Activity Handler` class includes various Microsoft Teams specific activity handlers that will be discussed in this article. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] -# [C#](#tab/csharp) +## Teams activity handler -As with any bot created using the Microsoft Bot Framework, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the `OnMessageActivityAsync` activity handler. This functionality remains the same, however if the bot receives a conversation update activity, the turn handler would see that incoming activity and send it to the `OnConversationUpdateActivityAsync` _Teams_ activity handler that will first check for any Teams specific events and pass it along to the Bot Framework's activity handler if none are found. +To create a bot for Teams, derive your bot from the _Teams activity handler_ class. When such a bot receives an activity, it routes the activity through various _activity handlers_. The initial, base handler is the _turn handler_, and it routes the activity to a handler based on the activity's type. The _turn handler_ calls the handler that is designed to handle the specific type of activity that was received. The _Teams activity handler_ class is derived from the _activity handler_ class. In addition to the activity types that the _activity handler_ can process, the Teams activity handler class includes additional handlers for Teams-specific activities. -In the Teams activity handler class there are two primary Teams activity handlers, `OnConversationUpdateActivityAsync` that routes all conversation update activities, and `OnInvokeActivityAsync` that routes all Teams invoke activities. All of the activity handlers described in the [Bot logic](https://aka.ms/how-bots-work#bot-logic) section of the `How bots work` article will continue to work as they do with a non-Teams bot, with the exception of handling the members added and removed activities, these will be different in the context of a team, where the new member is added to the team as opposed to a message thread. See the _Teams conversation update activities_ table in the [Bot logic](#bot-logic) section for more details. +A bot that derives from the Teams activity handler is similar to a bot that derives directly from the activity handler class. +However, Teams includes additional information in `conversationUpdate` activities and sends Teams-specific `invoke` and `event` activities. -To implement your logic for these Teams specific activity handlers, you will override these methods in your bot as shown in the [Bot logic](#bot-logic) section below. For each of these handlers, there is no base implementation, so just add the logic that you want in your override. + + +### [C#](#tab/csharp) + +When your Teams activity handler–bot receives a message activity, its turn handler routes the incoming message activity to its `OnMessageActivityAsync` handler, similar to how an activity handler–based bot would. However, when your Teams bot receives a conversation update activity, the Teams version of the `OnConversationUpdateActivityAsync` handler processes the activity. + +There's no base implementation for most of the Teams-specific activity handlers. You'll need to override these handlers and provide appropriate logic for your bot. + +### [JavaScript](#tab/javascript) + +When your Teams activity handler–bot receives a message activity, its turn handler routes the incoming message activity to its `onMessage` handler, similar to how an activity handler–based bot would. However, when your Teams bot receives a conversation update activity, the Teams version of the `dispatchConversationUpdateActivity` handler processes the activity. + +There's no base implementation for most of the Teams-specific activity handlers. You'll need to override these handlers and provide appropriate logic for your bot. + +When overriding these Teams-specific activity handlers, define your bot logic, then **be sure to call `next()` at the end**. By calling `next()`, you ensure that the next handler is run. + +### [Java](#tab/java) -# [JavaScript](#tab/javascript) +When your Teams activity handler–bot receives a message activity, its turn handler routes the incoming message activity to its `onMessageActivity` handler, similar to how an activity handler–based bot would. However, when your Teams bot receives a conversation update activity, the Teams version of the `onConversationUpdateActivity` handler processes the activity. -As with any bot created using the Microsoft Bot Framework, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the `onMessage` activity handler. This functionality remains the same, however if the bot receives a conversation update activity, the turn handler would see that incoming activity and send it to the `dispatchConversationUpdateActivity` _Teams_ activity handler that will first check for any Teams specific events and pass it along to the Bot Framework's activity handler if none are found. +There's no base implementation for most of the Teams-specific activity handlers. You'll need to override these handlers and provide appropriate logic for your bot. -In the Teams activity handler class there are two primary Teams activity handlers, `dispatchConversationUpdateActivity` that routes all conversation update Activities, and `onInvokeActivity` that routes all Teams invoke activities. All of the activity handlers described in the [Bot logic](https://aka.ms/how-bots-work#bot-logic) section of the `How bots work` article will continue to work as they do with a non-Teams bot, with the exception of handling the members added and removed activities, these will be different in the context of a team, where the new member is added to the team as opposed to a message thread. See the _Teams conversation update activities_ table in the [Bot logic](#bot-logic) section for more details. +### [Python](#tab/python) -As with any bot handler, to implement your logic for the Teams handlers, you will override the methods in your bot as seen in the [Bot logic](#bot-logic) section below. For each of these handlers, define your bot logic, then **be sure to call `next()` at the end**. By calling `next()` you ensure that the next handler is run. +When your Teams activity handler–bot receives a message activity, its turn handler routes the incoming message activity to its `on_message_activity` handler, similar to how an activity handler–based bot would. However, when your Teams bot receives a conversation update activity, the Teams version of the `on_conversation_update_activity` handler processes the activity. + +There's no base implementation for most of the Teams-specific activity handlers. You'll need to override these handlers and provide appropriate logic for your bot. --- -### Bot logic +All of the activity handlers described in the [activity handling](bot-activity-handler-concept.md#activity-handling) section of the _Event-driven conversations using an activity handler_ article will continue to work as they do with a non-Teams bot, except for handling the members added and removed activities, these activities will be different in the context of a team, where the new member is added to the team as opposed to a message thread. For more information, see [Teams conversation update activities](#teams-conversation-update-activities). + +To implement your logic for these Teams-specific activity handlers, you'll override methods in your bot. + +## Teams-bot logic The bot logic processes incoming activities from one or more of your bots channels and generates outgoing activities in response. This is still true of bot derived from the Teams activity handler class, which first checks for Teams activities, then passes all other activities to the Bot Framework's activity handler. -# [C#](#tab/csharp) +### Teams installation update activities + +Add a handler for the _installation update_ event to let your bot: + +- Send an introductory message when it's installed on a conversation thread. +- Clean up user and thread data when it's uninstalled from a thread. + +See [Installation update event](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#installation-update-event) in the Teams docs for more information. + +### Teams conversation update activities + +The following table lists the Teams events that generate a _conversation update_ activity in a bot. +The Microsoft Teams [Conversation update events](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events) article describes how to use each of these events. + +### [C#](#tab/csharp) + +Below is a list of all of the Teams activity handlers called from the `OnConversationUpdateActivityAsync` method of the _Teams_ activity handler. + +| EventType | Handler | Condition | Teams documentation | +|:-|:-|:-|:-| +| channelCreated | `OnTeamsChannelCreatedAsync` | Sent whenever a new channel is created in a team your bot is installed in. | [Channel created](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-created). | +| channelDeleted | `OnTeamsChannelDeletedAsync` | Sent whenever a channel is deleted in a team your bot is installed in. | [Channel deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-deleted). | +| channelRenamed | `OnTeamsChannelRenamedAsync` | Sent whenever a channel is renamed in a team your bot is installed in. | [Channel renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-renamed). | +| channelRestored | `OnTeamsChannelRestoredAsync` | Sent whenever a channel that was previously deleted is restored in a team that your bot is already installed in. | [Channel restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-restored). | +| membersAdded | `OnTeamsMembersAddedAsync` | By default, calls the `ActivityHandler.OnMembersAddedAsync` method. Sent the first time your bot is added to a conversation and every time a new user is added to a team or group chat that your bot is installed in. | [Team members added](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-added). | +| membersRemoved | `OnTeamsMembersRemovedAsync` | By default, calls the `ActivityHandler.OnMembersRemovedAsync` method. Sent if your bot is removed from a team and every time any user is removed from a team that your bot is a member of. | [Team members removed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-removed). | +| teamArchived | `OnTeamsTeamArchivedAsync` | Sent when the team your bot is installed in is archived. | [Team archived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-archived). | +| teamDeleted | `OnTeamsTeamDeletedAsync` | Sent when the team your bot is in has been deleted. | [Team deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-deleted). | +| teamRenamed | `OnTeamsTeamRenamedAsync` | Sent when the team your bot is in has been renamed. | [Team renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-renamed). | +| teamRestored | `OnTeamsTeamRestoredAsync` | Sent when a previously deleted team your bot is in is restored. | [Team restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-restored). | +| teamUnarchived | `OnTeamsTeamUnarchivedAsync` | Sent when the team your bot is installed in is unarchived.| [Team unarchived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-unarchived). | + +### [JavaScript](#tab/javascript) + +Developers may handle conversation update activities sent from Microsoft Teams via two methods: + +1. To pass in a callback, use methods that begin with `on` _and_ end with `Event` (for example, the `onTeamsMembersAddedEvent` method). +1. When creating a derived class, override methods that begin with `on` and _don't_ end with `Event` (for example, the `onTeamsMembersAdded` method). + +Developers should use only one of these options: either 1 or 2, and not _both_ for the same activity. Meaning, developers should either pass a callback to the `onTeamsMembersAddedEvent` method _or_ override the `onTeamsMembersAdded` method in a derived class, and not do both. + +#### Methods for registering a callback + +Below is a list of all of the Teams activity emitters called from the `dispatchConversationUpdateActivity` method of the _Teams_ activity handler. + +| EventType | Registration method | Condition | Teams documentation | +|:-|:-|:-|:-| +| channelCreated | `onTeamsChannelCreatedEvent` | Sent whenever a new channel is created in a team your bot is installed in. | [Channel created](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-created). | +| channelDeleted | `onTeamsChannelDeletedEvent` | Sent whenever a channel is deleted in a team your bot is installed in. | [Channel deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-deleted). | +| channelRenamed | `onTeamsChannelRenamedEvent` | Sent whenever a channel is renamed in a team your bot is installed in. | [Channel renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-renamed). | +| channelRestored | `onTeamsChannelRestoredEvent` | Sent whenever a channel that was previously deleted is restored in a team that your bot is already installed in. | [Channel restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-restored). | +| membersAdded | `onTeamsMembersAddedEvent` | Sent the first time your bot is added to a conversation and every time a new user is added to a team or group chat that your bot is installed in. | [Team members added](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-added). | +| membersRemoved | `onTeamsMembersRemovedEvent` | Sent if your bot is removed from a team and every time any user is removed from a team that your bot is a member of. | [Team members removed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-removed). | +| teamArchived | `onTeamsTeamArchivedEvent` | Sent when the team your bot is installed in is archived. | [Team archived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-archived). | +| teamDeleted | `onTeamsTeamDeletedEvent` | Sent when the team your bot is in has been deleted. | [Team deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-deleted). | +| teamRenamed | `onTeamsTeamRenamedEvent` | Sent when the team your bot is in has been renamed. | [Team renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-renamed). | +| teamRestored | `onTeamsTeamrestoredEvent` | Sent when a previously deleted team your bot is in is restored. | [Team restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-restored). | +| teamUnarchived | `onTeamsTeamUnarchivedEvent` | Sent when the team your bot is installed in is unarchived.| [Team unarchived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-unarchived). | + +#### Methods to override in a derived class + +Below is a list of all of the Teams activity handlers that can be overridden to handle Teams conversation update activities. + +| EventType | Handler | Condition | Teams documentation | +|:-|:-|:-|:-| +| channelCreated | `onTeamsChannelCreated` | Sent whenever a new channel is created in a team your bot is installed in. | [Channel created](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-created). | +| channelDeleted | `onTeamsChannelDeleted` | Sent whenever a channel is deleted in a team your bot is installed in. | [Channel deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-deleted). | +| channelRenamed | `onTeamsChannelRenamed` | Sent whenever a channel is renamed in a team your bot is installed in. | [Channel renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-renamed). | +| channelRestored | `onTeamsChannelRestored` | Sent whenever a channel that was previously deleted is restored in a team that your bot is already installed in. | [Channel restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-restored). | +| membersAdded | `onTeamsMembersAdded` | By default, calls the `ActivityHandler.onMembersAdded` method. Sent the first time your bot is added to a conversation and every time a new user is added to a team or group chat that your bot is installed in. | [Team members added](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-added). | +| membersRemoved | `onTeamsMembersRemoved` | By default, calls the `ActivityHandler.onMembersRemoved` method. Sent if your bot is removed from a team and every time any user is removed from a team that your bot is a member of. | [Team members removed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-removed). | +| teamArchived | `onTeamsTeamArchived` | Sent when the team your bot is installed in is archived. | [Team archived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-archived). | +| teamDeleted | `onTeamsTeamDeleted` | Sent when the team your bot is in has been deleted. | [Team deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-deleted). | +| teamRenamed | `onTeamsTeamRenamed` | Sent when the team your bot is in has been renamed. | [Team renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-renamed). | +| teamRestored | `onTeamsTeamRestored` | Sent when a previously deleted team your bot is in is restored. | [Team restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-restored). | +| teamUnarchived | `onTeamsTeamUnarchived` | Sent when the team your bot is installed in is unarchived.| [Team unarchived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-unarchived). | + +### [Java](#tab/java) + +Below is a list of all of the Teams activity handlers called from the `onConversationUpdateActivity` method of the _Teams_ activity handler. + +| EventType | Handler | Condition | Teams documentation | +|:-|:-|:-|:-| +| channelCreated | `onTeamsChannelCreated` | Sent whenever a new channel is created in a team your bot is installed in. | [Channel created](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-created). | +| channelDeleted | `onTeamsChannelDeleted` | Sent whenever a channel is deleted in a team your bot is installed in. | [Channel deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-deleted). | +| channelRenamed | `onTeamsChannelRenamed` | Sent whenever a channel is renamed in a team your bot is installed in. | [Channel renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-renamed). | +| channelRestored | `onTeamsChannelRestored` | Sent whenever a channel that was previously deleted is restored in a team that your bot is already installed in. | [Channel restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-restored). | +| membersAdded | `onTeamsMembersAdded` | By default, calls the `ActivityHandler.onMembersAdded` method. Sent the first time your bot is added to a conversation and every time a new user is added to a team or group chat that your bot is installed in. | [Team members added](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-added). | +| membersRemoved | `onTeamsMembersRemoved` | By default, calls the `ActivityHandler.onMembersRemoved` method. Sent if your bot is removed from a team and every time any user is removed from a team that your bot is a member of. | [Team members removed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-removed). | +| teamArchived | `onTeamsTeamArchived` | Sent when the team your bot is installed in is archived. | [Team archived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-archived). | +| teamDeleted | `onTeamsTeamDeleted` | Sent when the team your bot is in has been deleted. | [Team deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-deleted). | +| teamRenamed | `onTeamsTeamRenamed` | Sent when the team your bot is in has been renamed. | [Team renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-renamed). | +| teamRestored | `onTeamsTeamRestored` | Sent when a previously deleted team your bot is in is restored. | [Team restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-restored). | +| teamUnarchived | `onTeamsTeamUnarchived` | Sent when the team your bot is installed in is unarchived.| [Team unarchived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-unarchived). | + +### [Python](#tab/python) + +Below is a list of all of the Teams activity handlers called from the `on_conversation_update_activity` method of the _Teams_ activity handler. + +| EventType | Handler | Condition | Teams documentation | +|:-|:-|:-|:-| +| channelCreated | `on_teams_channel_created` | Sent whenever a new channel is created in a team your bot is installed in. | [Channel created](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-created). | +| channelDeleted | `on_teams_channel_deleted` | Sent whenever a channel is deleted in a team your bot is installed in. | [Channel deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-deleted). | +| channelRenamed | `on_teams_channel_renamed` | Sent whenever a channel is renamed in a team your bot is installed in. | [Channel renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-renamed). | +| channelRestored | `on_teams_channel_restored` | Sent whenever a channel that was previously deleted is restored in a team that your bot is already installed in. | [Channel restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#channel-restored). | +| membersAdded | `on_teams_members_added` | By default, calls the base class `on_members_added_activity` method. Sent the first time your bot is added to a conversation and every time a new user is added to a team or group chat that your bot is installed in. | [Team members added](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-added). | +| membersRemoved | `on_teams_members_removed` | By default, calls the base class `on_members_removed_activity` method. Sent if your bot is removed from a team and every time any user is removed from a team that your bot is a member of. | [Team members removed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-members-removed). | +| teamArchived | `on_teams_team_archived` | Sent when the team your bot is installed in is archived. | [Team archived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-archived). | +| teamDeleted | `on_teams_team_deleted` | Sent when the team your bot is in has been deleted. | [Team deleted](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-deleted). | +| teamRenamed | `on_teams_team_renamed` | Sent when the team your bot is in has been renamed. | [Team renamed](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-renamed). | +| teamRestored | `on_teams_team_restored` | Sent when a previously deleted team your bot is in is restored. | [Team restored](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-restored). | +| teamUnarchived | `on_teams_team_unarchived` | Sent when the team your bot is installed in is unarchived.| [Team unarchived](/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events#team-unarchived). | -#### Teams conversation update activities +--- -Below is a list of all of the Teams activity handlers called from the `OnConversationUpdateActivityAsync` _Teams_ activity handler. The [Conversation update events](https://aka.ms/azure-bot-subscribe-to-conversation-events) article describes how to use each of these events in a bot. +### Teams event activities -| Event | Handler | Description | -| :-- | :-- | :-- | -| channelCreated | `OnTeamsChannelCreatedAsync` | Override this to handle a Teams channel being created. For more information see [Channel created](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-created). | -| channelDeleted | `OnTeamsChannelDeletedAsync` | Override this to handle a Teams channel being deleted. For more information see [Channel deleted](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-deleted). | -| channelRenamed | `OnTeamsChannelRenamedAsync` | Override this to handle a Teams channel being renamed. For more information see [Channel renamed](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-renamed). | -| teamRenamed | `OnTeamsTeamRenamedAsync` | `return Task.CompletedTask;` Override this to handle a Teams Team being Renamed. For more information see [Team renamed](https://aka.ms/azure-bot-subscribe-to-conversation-events#team-renamed). | -| MembersAdded | `OnTeamsMembersAddedAsync` | Calls the `OnMembersAddedAsync` method in `ActivityHandler`. Override this to handle members joining a team. For more information see [Team member added](https://aka.ms/azure-bot-subscribe-to-conversation-events#Team-Member-Added).| -| MembersRemoved | `OnTeamsMembersRemovedAsync` | Calls the `OnMembersRemovedAsync` method in `ActivityHandler`. Override this to handle members leaving a team. For more information see [Team member removed](https://aka.ms/azure-bot-subscribe-to-conversation-events#Team-Member-Removed).| +The following table lists the Teams-specific event activities Teams sends to a bot. +The event activities listed are for conversational bots in Teams. +### [C#](#tab/csharp) - +These are the Teams-specific event activity handlers called from the `OnEventActivityAsync` _Teams_ activity handler. + +| Event types | Handler | Description | +|:---------------------------------------|:---------------------------|:--------------------------------------------------------| +| application/vnd.microsoft.meetingEnd | `OnTeamsMeetingEndAsync` | The bot is associated with a meeting that just ended. | +| application/vnd.microsoft.meetingStart | `OnTeamsMeetingStartAsync` | The bot is associated with a meeting that just started. | + +### [JavaScript](#tab/javascript) -#### Teams invoke activities +These are the Teams-specific event activity handlers called from the `onEventActivity` _Teams_ activity handler. -Here is a list of all of the Teams activity handlers called from the `OnInvokeActivityAsync` _Teams_ activity handler: +| Event types | Handler | Description | +|:---------------------------------------|:----------------------|:--------------------------------------------------------| +| application/vnd.microsoft.meetingEnd | `onTeamsMeetingEnd` | The bot is associated with a meeting that just ended. | +| application/vnd.microsoft.meetingStart | `onTeamsMeetingStart` | The bot is associated with a meeting that just started. | -| Invoke types | Handler | Description | -| :----------------------------- | :----------------------------------- | :----------------------------------------------------------- | -| CardAction.Invoke | `OnTeamsCardActionInvokeAsync` | Teams Card Action Invoke. | -| fileConsent/invoke | `OnTeamsFileConsentAcceptAsync` | Teams File Consent Accept. | -| fileConsent/invoke | `OnTeamsFileConsentAsync` | Teams File Consent. | -| fileConsent/invoke | `OnTeamsFileConsentDeclineAsync` | Teams File Consent. | +### [Java](#tab/java) + +These are the Teams-specific event activity handlers called from the `onEventActivity` _Teams_ activity handler. + +| Event types | Handler | Description | +|:---------------------------------------|:----------------------|:--------------------------------------------------------| +| application/vnd.microsoft.meetingEnd | `onTeamsMeetingEnd` | The bot is associated with a meeting that just ended. | +| application/vnd.microsoft.meetingStart | `onTeamsMeetingStart` | The bot is associated with a meeting that just started. | + +### [Python](#tab/python) + +These are the Teams-specific event activity handlers called from the `on_event_activity` _Teams_ activity handler. + +| Event types | Handler | Description | +|:---------------------------------------|:-------------------------------|:--------------------------------------------------------| +| application/vnd.microsoft.meetingEnd | `on_teams_meeting_end_event` | The bot is associated with a meeting that just ended. | +| application/vnd.microsoft.meetingStart | `on_teams_meeting_start_event` | The bot is associated with a meeting that just started. | + +--- + +### Teams invoke activities + +The following table lists the Teams-specific invoke activities Teams sends to a bot. +The invoke activities listed are for conversational bots in Teams. The Bot Framework SDK also supports invokes specific to messaging extensions. For more information, see the Teams [What are messaging extensions](/microsoftteams/platform/messaging-extensions/what-are-messaging-extensions) article. + +> [!NOTE] +> Microsoft Teams platform documentation and Teams JavaScript client library (TeamsJS) refer to task modules as modal *dialogs*. See [Dialogs](/microsoftteams/platform/task-modules-and-cards/what-are-task-modules) for more information. + +### [C#](#tab/csharp) + +Here's a list of all of the Teams activity handlers called from the `OnInvokeActivityAsync` _Teams_ activity handler: + +| Invoke types | Handler | Description | +|:--------------------------------|:--------------------------------------|:----------------------------------| | actionableMessage/executeAction | `OnTeamsO365ConnectorCardActionAsync` | Teams O365 Connector Card Action. | -| signin/verifyState | `OnTeamsSigninVerifyStateAsync` | Teams Sign in Verify State. | -| task/fetch | `OnTeamsTaskModuleFetchAsync` | Teams Task Module Fetch. | -| task/submit | `OnTeamsTaskModuleSubmitAsync` | Teams Task Module Submit. | - -The invoke activities listed above are for conversational bots in Teams. The Bot Framework SDK also supports invokes specific to messaging extensions. For more information see [What are messaging extensions](https://aka.ms/azure-bot-what-are-messaging-extensions) - -# [JavaScript](#tab/javascript) - -#### Teams conversation update activities - -Below is a list of all of the Teams activity handlers called from the `dispatchConversationUpdateActivity` _Teams_ activity handler. The [Conversation update events](https://aka.ms/azure-bot-subscribe-to-conversation-events) article describes how to use each of these events in a bot. - -| Event | Handler | Description | -| :-- | :-- | :-- | -| channelCreated | `OnTeamsChannelCreatedEvent` | Override this to handle a Teams channel being created. For more information see [Channel created](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-created). | -| channelDeleted | `OnTeamsChannelDeletedEvent` | Override this to handle a Teams channel being deleted. For more information see [Channel deleted](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-deleted).| -| channelRenamed | `OnTeamsChannelRenamedEvent` | Override this to handle a Teams channel being renamed. For more information see [Channel renamed](https://aka.ms/azure-bot-subscribe-to-conversation-events#channel-renamed). | -| teamRenamed | `OnTeamsTeamRenamedEvent` | `return Task.CompletedTask;` Override this to handle a Teams Team being Renamed. For more information see [Team renamed](https://aka.ms/azure-bot-subscribe-to-conversation-events#team-renamed). | -| MembersAdded | `OnTeamsMembersAddedEvent` | Calls the `OnMembersAddedEvent` method in `ActivityHandler`. Override this to handle members joining a team. For more information see [Team member added](https://aka.ms/azure-bot-subscribe-to-conversation-events#Team-Member-Added). | -| MembersRemoved | `OnTeamsMembersRemovedEvent` | Calls the `OnMembersRemovedEvent` method in `ActivityHandler`. Override this to handle members leaving a team. For more information see [Team member removed](https://aka.ms/azure-bot-subscribe-to-conversation-events#Team-Member-Removed). | - - +| CardAction.Invoke | `OnTeamsCardActionInvokeAsync` | Teams Card Action Invoke. | +| fileConsent/invoke | `OnTeamsFileConsentAcceptAsync` | Teams File Consent Accept. | +| fileConsent/invoke | `OnTeamsFileConsentAsync` | Teams File Consent. | +| fileConsent/invoke | `OnTeamsFileConsentDeclineAsync` | Teams File Consent. | +| signin/verifyState | `OnTeamsSigninVerifyStateAsync` | Teams Sign in Verify State. | +| task/fetch | `OnTeamsTaskModuleFetchAsync` | Teams Task Module Fetch. | +| task/submit | `OnTeamsTaskModuleSubmitAsync` | Teams Task Module Submit. | -#### Teams invoke activities +### [JavaScript](#tab/javascript) -Here is a list of all of the Teams activity handlers called from the `onInvokeActivity` _Teams_ activity handler: +Here's a list of all of the Teams activity handlers called from the `onInvokeActivity` _Teams_ activity handler: -| Invoke types | Handler | Description | -| :----------------------------- | :----------------------------------- | :----------------------------------------------------------- | -| CardAction.Invoke | `handleTeamsCardActionInvoke` | Teams Card Action Invoke. | -| fileConsent/invoke | `handleTeamsFileConsentAccept` | Teams File Consent Accept. | -| fileConsent/invoke | `handleTeamsFileConsent` | Teams File Consent. | -| fileConsent/invoke | `handleTeamsFileConsentDecline` | Teams File Consent. | +| Invoke types | Handler | Description | +|:--------------------------------|:-------------------------------------|:----------------------------------| | actionableMessage/executeAction | `handleTeamsO365ConnectorCardAction` | Teams O365 Connector Card Action. | -| signin/verifyState | `handleTeamsSigninVerifyState` | Teams Sign in Verify State. | -| task/fetch | `handleTeamsTaskModuleFetch` | Teams Task Module Fetch. | -| task/submit | `handleTeamsTaskModuleSubmit` | Teams Task Module Submit. | - -The invoke activities listed above are for conversational bots in Teams. The Bot Framework SDK also supports invokes specific to messaging extensions. For more information see [What are messaging extensions](https://aka.ms/azure-bot-what-are-messaging-extensions) +| CardAction.Invoke | `handleTeamsCardActionInvoke` | Teams Card Action Invoke. | +| fileConsent/invoke | `handleTeamsFileConsentAccept` | Teams File Consent Accept. | +| fileConsent/invoke | `handleTeamsFileConsent` | Teams File Consent. | +| fileConsent/invoke | `handleTeamsFileConsentDecline` | Teams File Consent. | +| signin/verifyState | `handleTeamsSigninVerifyState` | Teams Sign in Verify State. | +| task/fetch | `handleTeamsTaskModuleFetch` | Teams Task Module (modal dialog) Fetch. | +| task/submit | `handleTeamsTaskModuleSubmit` | Teams Task Module (modal dialog) Submit. | + +### [Java](#tab/java) + +Here's a list of all of the Teams activity handlers called from the `onInvokeActivity` _Teams_ activity handler: + +| Invoke types | Handler | Description | +|:--------------------------------|:--------------------------------------|:----------------------------------| +| fileConsent/invoke | `onTeamsFileConsent` | Teams File Consent. | +| actionableMessage/executeAction | `onTeamsO365ConnectorCardAction` | Teams O365 Connector Card Action. | +| task/fetch | `onTeamsTaskModuleFetch` | Teams Task Module Fetch. | +| task/submit | `onTeamsTaskModuleSubmit` | Teams Task Module Submit. | +| tab/fetch | `onTeamsTabFetch` | Teams Tab Fetch. | +| tab/submit | `onTeamsTabSubmit` | Teams Tab Submit. | +| CardAction.Invoke | `onTeamsCardActionInvoke` | Teams Card Action Invoke. | +| fileConsent/invoke | `onTeamsFileConsentAccept` | Teams File Consent Accept. | +| fileConsent/invoke | `onTeamsFileConsentDecline` | Teams File Consent. | +| signin/verifyState | `onTeamsSigninVerifyState` | Teams Sign in Verify State. | + +### [Python](#tab/python) + +Here's a list of all of the Teams activity handlers called from the `on_invoke_activity` _Teams_ activity handler: + +| Invoke types | Handler | Description | +|:--------------------------------|:--------------------------------------|:----------------------------------| +| actionableMessage/executeAction | `on_teams_o365_connector_card_action` | Teams O365 Connector Card Action. | +| CardAction.Invoke | `on_teams_card_action_invoke` | Teams Card Action Invoke. | +| fileConsent/invoke | `on_teams_file_consent_accept` | Teams File Consent Accept. | +| fileConsent/invoke | `on_teams_file_consent` | Teams File Consent. | +| fileConsent/invoke | `on_teams_file_consent_decline` | Teams File Consent Decline. | +| signin/verifyState | `on_teams_signin_verify_state` | Teams Sign in Verify State. | +| task/fetch | `on_teams_task_module_fetch` | Teams Task Module Fetch. | +| task/submit | `on_teams_task_module_submit` | Teams Task Module Submit. | --- ## Next steps -For building Teams bots, refer to Microsoft Teams Developer [documentation](https://aka.ms/teams-docs). \ No newline at end of file + +For building Teams bots, refer to Microsoft Teams Developer [documentation](/microsoftteams/platform/bots/how-to/create-a-bot-for-teams). diff --git a/articles/v4sdk/bot-builder-basics.md b/articles/v4sdk/bot-builder-basics.md index 7f536fbef..3883d0fbe 100644 --- a/articles/v4sdk/bot-builder-basics.md +++ b/articles/v4sdk/bot-builder-basics.md @@ -1,538 +1,229 @@ --- -title: How bots work - Bot Service -description: Describes how activity and http work within the Bot Framework SDK. -keywords: conversation flow, turn, bot conversation, dialogs, prompts, waterfalls, dialog set -author: johnataylor -ms.author: johtaylo -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/31/2020 +title: Basics of the Microsoft Bot Framework +description: Become familiar with the Microsoft Bot Framework. Understand how bots communicate with users, and learn about activities, channels, HTTP POST requests, and more. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: overview monikerRange: 'azure-bot-service-4.0' +ms.custom: + - abs-meta-21q1 + - evergreen --- -# How bots work +# Basics of the Microsoft Bot Framework -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -A bot is an app that users interact with in a conversational way, using text, graphics (such as cards or images), or speech. Every interaction between the user and the bot generates an *activity*. The Bot Framework Service, which is a component of the Azure Bot Service, sends information between the user's bot-connected app (such as Facebook, Skype, Slack, etc. which we call the *channel*) and the bot. Each channel may include additional information in the activities they send. Before creating bots, it is important to understand how a bot uses activity objects to communicate with its users. Let's first take a look at activities that are exchanged when we run a simple echo bot. +A bot is an app that users interact with in a conversational way, using text, graphics (such as cards or images), or speech. Azure AI Bot Service is a cloud platform. It hosts bots and makes them available to _channels_, such as Microsoft Teams, Facebook, or Slack. -![activity diagram](media/bot-builder-activity.png) +The Bot Framework Service, which is a component of the Azure AI Bot Service, sends information between the user's bot-connected app and the bot. Each channel can include additional information in the activities they send. Before creating bots, it's important to understand how a bot uses activity objects to communicate with its users. -Two activity types illustrated here are: *conversation update* and *message*. +This diagram illustrates two activity types, _conversation update_ and _message_, that might be exchanged when a user communicates with an echo bot. -The Bot Framework Service may send a conversation update when a party joins the conversation. For example, on starting a conversation with the Bot Framework Emulator, you will see two conversation update activities (one for the user joining the conversation and one for the bot joining). To distinguish these conversation update activities, check whether the *members added* property includes a member other than the bot. +:::image type="content" source="media/bot-builder-activity.png" alt-text="activity diagram"::: -The message activity carries conversation information between the parties. In an echo bot example, the message activities are carrying simple text and the channel will render this text. Alternatively, the message activity might carry text to be spoken, suggested actions or cards to be displayed. +The Bot Framework Service sends a _conversation update_ when a party joins the conversation. For example, on starting a conversation with the Bot Framework Emulator, you might see two conversation update activities (one for the user joining the conversation and one for the bot joining). To distinguish these conversation update activities, check who is included in the _members added_ property of the activity. -In this example, the bot created and sent a message activity in response to the inbound message activity it had received. However, a bot can respond in other ways to a received message activity; it’s not uncommon for a bot to respond to a conversation update activity by sending some welcome text in a message activity. More information can be found in [welcoming the user](bot-builder-welcome-user.md). +The _message_ activity carries conversation information between the parties. In an echo bot example, the message activities are carrying simple text and the channel will render this text. Alternatively, the message activity might carry text to be spoken, suggested actions or cards to be displayed. -### HTTP Details +> [!TIP] +> It's up to each channel to implement the Bot Framework protocol, and how each channel does so might be a little different. For example, some channels send conversation update activities first, and some send conversation update activities after they send the first message activity. A channel might include both the bot and user in one conversation update activity, while another might send two conversation update activities. -Activities arrive at the bot from the Bot Framework Service via an HTTP POST request. The bot responds to the inbound POST request with a 200 HTTP status code. Activities sent from the bot to the channel are sent on a separate HTTP POST to the Bot Framework Service. This, in turn, is acknowledged with a 200 HTTP status code. - -The protocol doesn’t specify the order in which these POST requests and their acknowledgments are made. However, to fit with common HTTP service frameworks, typically these requests are nested, meaning that the outbound HTTP request is made from the bot within the scope of the inbound HTTP request. This pattern is illustrated in the diagram above. Since there are two distinct HTTP connections back to back, the security model must provide for both. - -### Defining a turn - -In a conversation, people often speak one-at-a-time, taking turns speaking. With a bot, it generally reacts to user input. Within the Bot Framework SDK, a _turn_ consists of the user's incoming activity to the bot and any activity the bot sends back to the user as an immediate response. You can think of a turn as the processing associated with the arrival of a given activity. - -The *turn context* object provides information about the activity such as the sender and receiver, the channel, and other data needed to process the activity. It also allows for the addition of information during the turn across various layers of the bot. - -The turn context is one of the most important abstractions in the SDK. Not only does it carry the inbound activity to all the middleware components and the application logic but it also provides the mechanism whereby the middleware components and the application logic can send outbound activities. +In this example, the bot created and sent a message activity in response to the inbound message activity it had received. However, a bot can respond in other ways to a received message activity, and it's common for a bot to respond to a conversation update activity by sending a message activity with a welcome message. For more information, see how to [welcome a user](bot-builder-welcome-user.md). -## The activity processing stack - -Let's drill into the previous diagram with a focus on the arrival of a message activity. - -![activity processing stack](media/bot-builder-activity-processing-stack.png) +## The Bot Framework SDK -In the example above, the bot replied to the message activity with another message activity containing the same text message. Processing starts with the HTTP POST request, with the activity information carried as a JSON payload, arriving at the web server. In C# this will typically be an ASP.NET project, in a JavaScript Node.js project this is likely to be one of the popular frameworks such as Express or Restify. +The Bot Framework SDK allows you to build bots that can be hosted on the Azure AI Bot Service. The service defines a REST API and an activity protocol for how your bot and channels or users can interact. The SDK builds upon this REST API and provides an abstraction of the service so that you can focus on the conversational logic. While you don't need to understand the REST service to use the SDK, understanding some of its features can be helpful. -The *adapter*, an integrated component of the SDK, is the core of the SDK runtime. The activity is carried as JSON in the HTTP POST body. This JSON is deserialized to create the Activity object that is then handed to the adapter with a call to *process activity* method. On receiving the activity, the adapter creates a *turn context* and calls the middleware. +Bots are apps that have a conversational interface. They can be used to shift simple, repetitive tasks, such as taking a dinner reservation or gathering profile information, on to automated systems that may no longer require direct human intervention. Users converse with a bot using text, interactive cards, and speech. A bot interaction can be a quick question and answer, or it can be a sophisticated conversation that intelligently provides access to services. -As mentioned above, the turn context provides the mechanism for the bot to send outbound activities, most often in response to an inbound activity. To achieve this, the turn context provides _send, update, and delete activity_ response methods. Each response method runs in an asynchronous process. +> [!NOTE] +> Support for features provided by the SDK and REST API varies by channel. +> You can test your bot using the Bot Framework Emulator, but you should also test all features of your bot on each channel in which you intend to make your bot available. -[!INCLUDE [alert-await-send-activity](../includes/alert-await-send-activity.md)] +Interactions involve the exchange of _activities_, which are handled in _turns_. -## Activity handlers +### Activities -When the bot receives an activity, it passes it on to its *activity handlers*. Under the covers, there is one base handler called the *turn handler*. All activities get routed through there. That turn handler then calls the individual activity handler for whatever type of activity it received. +Every interaction between the user (or a channel) and the bot is represented as an _activity_. +The Bot Framework [Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) defines the activities that can be exchanged between a user or channel and a bot. Activities can represent human text or speech, app-to-app notifications, reactions to other messages, and so on. -# [C#](#tab/csharp) + -For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the `OnMessageActivityAsync` activity handler. +### Turns -When building your bot, your bot logic for handling and responding to messages will go in this `OnMessageActivityAsync` handler. Likewise, your logic for handling members being added to the conversation will go in your `OnMembersAddedAsync` handler, which is called whenever a member is added to the conversation. +In a conversation, people often speak one-at-a-time, taking turns speaking. With a bot, it generally reacts to user input. Within the Bot Framework SDK, a _turn_ consists of the user's incoming activity to the bot and any activity the bot sends back to the user as an immediate response. You can think of a turn as the processing associated with the bot receiving a given activity. -To implement your logic for these handlers, you will override these methods in your bot as seen in the [Bot logic](#bot-logic) section below. For each of these handlers, there is no base implementation, so just add the logic that you want in your override. +For example, a user might ask a bot to perform a certain task. The bot might respond with a question to get more information about the task, at which point this turn ends. On the next turn, the bot receives a new message from the user that might contain the answer to the bot's question, or it might represent a change of subject or a request to ignore the initial request to perform the task. -There are certain situations where you will want to override the base turn handler, such as [saving state](bot-builder-concept-state.md) at the end of a turn. When doing so, be sure to first call `await base.OnTurnAsync(turnContext, cancellationToken);` to make sure the base implementation of `OnTurnAsync` is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as `OnMessageActivityAsync`. +## Bot application structure -# [JavaScript](#tab/javascript) +The SDK defines a _bot_ class that handles the conversational reasoning for the bot app. The bot class: -The JavaScript `ActivityHandler` uses an event emitter and listener pattern. -For example, use the `onMessage` method to register an event listener for message activities. You can register more than one listener. When the bot receives a message activity, the activity handler would see that incoming activity and send it each of the `onMessage` activity listeners, in the order in which they were registered. +- Recognizes and interprets the user's input. +- Reasons about the input and performs relevant tasks. +- Generates responses about what the bot is doing or has done. -When building your bot, your bot logic for handling and responding to messages will go in the `onMessage` listeners. Likewise, your logic for handling members being added to the conversation will go in your `onMembersAdded` listeners, which are called whenever a member is added to the conversation. -To add these listeners, you will register them in your bot as seen in the [Bot logic](#bot-logic) section below. For each listener, include your bot logic, then **be sure to call `next()` at the end**. By calling `next()` you ensure that the next listener is run. +The SDK also defines an _adapter_ class that handles connectivity with the channels. The adapter: -Make sure to [save state](bot-builder-concept-state.md) before the turn ends. You can do so by overriding the activity handler `run` method and saving state after the parent's `run` method completes. +- Provides a method for handling requests from and methods for generating requests to the user's channel. +- Includes a middleware pipeline, which includes turn processing outside of your bot's turn handler. +- Calls the bot's turn handler and catches errors not otherwise handled in the turn handler. -There aren't any common situations where you will want to override the base turn handler, so be careful if you try to do so. -There is a special handler called `onDialog`. The `onDialog` handler runs at the end, after the rest of the handlers have run, and is not tied to a certain activity type. As with all the above handlers, be sure to call `next()` to ensure the rest of the process wraps up. +In addition, bots often need to retrieve and store state each turn. +State is handled through _storage_, _bot state_, and _property accessor_ classes. +The SDK doesn't provide built-in storage, but does provide abstractions for storage and a few implementations of a storage layer. +The [managing state](bot-builder-concept-state.md) topic describes these state and storage features. -# [Python](#tab/python) +:::image type="content" source="../media/bot-builder-basics/how-bots-work.png" alt-text="A bot has connectivity and reasoning elements, and an abstraction for state"::: -For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the `on_message_activity` activity handler. +The SDK doesn't require you use a specific application layer to send and receive web requests. +The Bot Framework has templates and samples for ASP.NET (C#), restify (JavaScript), and aiohttp (Python). +However, you can choose to use a different application layer for your app. -When building your bot, your bot logic for handling and responding to messages will go in this `on_message_activity` handler. Likewise, your logic for handling members being added to the conversation will go in your `on_members_added` handler, which is called whenever a member is added to the conversation. +When you create a bot using the SDK, you provide the code to receive the HTTP traffic and forward it to the adapter. The Bot Framework provides a few templates and samples that you can use to develop your own bots. -To implement your logic for these handlers, you will override these methods in your bot as seen in the [Bot logic](#bot-logic) section below. For each of these handlers, there is no base implementation, so just add the logic that you want in your override. - -There are certain situations where you will want to override the base turn handler, such as [saving state](bot-builder-concept-state.md) at the end of a turn. When doing so, be sure to first call `await super().on_turn(turnContext);` to make sure the base implementation of `on_turn` is run before your additional code. That base implementation is, among other things, responsible for calling the rest of the activity handlers such as `on_message_activity`. - ---- +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] -## Middleware - -Middleware is much like any other messaging middleware, comprising a linear set of components that are each executed in order, giving each a chance to operate on the activity. The final stage of the middleware pipeline is a callback to the turn handler on the bot class the application has registered with the adapter's *process activity* method. The turn handler is generally `OnTurnAsync` in C# and `onTurn` in JavaScript. - -The turn handler takes a turn context as its argument, typically the application logic running inside the turn handler function will process the inbound activity’s content and generate one or more activities in response, sending these out using the *send activity* function on the turn context. Calling *send activity* on the turn context will cause the middleware components to be invoked on the outbound activities. Middleware components execute before and after the bot’s turn handler function. The execution is inherently nested and, as such, sometimes referred to being like a Russian Doll. For more in depth information about middleware, see the [middleware topic](~/v4sdk/bot-builder-concept-middleware.md). - -## Bot structure - -In the following sections, we examine _key pieces_ of an EchoBot that you can easily create using the templates provided for [**CSharp**](../dotnet/bot-builder-dotnet-sdk-quickstart.md) or [**JavaScript**](../javascript/bot-builder-javascript-quickstart.md). +### Bot logic - +The bot object contains the conversational reasoning or logic for a turn and exposes a _turn handler_, which is the method that can accept incoming activities from the bot adapter. -A bot is a web application, and we provide templates for each language. +The SDK provides a couple different paradigms for managing your bot logic. -# [C#](#tab/csharp) +- _Activity handlers_ provide an event-driven model in which the incoming activity types and subtypes are the events. Consider an activity handler for bots that have limited, short interactions with the user. + - Use an [activity handler](bot-activity-handler-concept.md) and implement handlers for each activity type or subtype your bot will recognize and react to. + - Use a [Teams activity handler](bot-builder-basics-teams.md) to create bots that can connect to the Teams channel. (The Teams channel requires the bot to handle some channel-specific behavior.) +- The [dialogs library](bot-builder-concept-dialog.md) provides a state-based model to manage a long-running conversation with the user. + - Use an activity handler and a _component dialog_ for largely sequential conversations. + For more information, see [about component and waterfall dialogs](bot-builder-concept-waterfall-dialogs.md). +- Implement your own bot class and provide your own logic for handling each turn. For an example, see how to [create your own prompts to gather user input](bot-builder-primitive-prompts.md). -The VSIX template generates a [ASP.NET MVC Core](https://dotnet.microsoft.com/apps/aspnet/mvc) web app. If you look at the [ASP.NET](https://docs.microsoft.com/aspnet/core/fundamentals/index?view=aspnetcore-2.1&tabs=aspnetcore2x) fundamentals, you'll see similar code in files such as **Program.cs** and **Startup.cs**. These files are required for all web apps and are not bot specific. +### The bot adapter -### appsettings.json file +The adapter has a _process activity_ method for starting a turn. -The **appsettings.json** file specifies the configuration information for your bot, such as the app ID, and password among other things. If using certain technologies or using this bot in production, you will need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. +- It takes the request body (the request payload, translated to an activity) and the request header as arguments. +- It checks whether the authentication header is valid. +- It creates a _context_ object for the turn. The context object includes information about the activity. +- It sends the context object through its _middleware_ pipeline. +- It then sends the context object to the bot object's turn handler. -# [JavaScript](#tab/javascript) +The adapter also: - +- Formats and sends response activities. These responses are typically messages for the user, but can also include information to be consumed by the user's channel directly. +- Surfaces other methods provided by the Bot Connector REST API, such as _update message_ and _delete message_. +- Catches errors or exceptions not otherwise caught for the turn. -The Yeoman generator creates a type of [restify](http://restify.com/) web application. If you look at the restify quickstart in their docs, you'll see an app similar to the generated **index.js** file. We describe some of the key files generated by the template. Code in some files won't be copied, but you will see it when you run the bot, and you can refer to the [Node.js echobot](https://aka.ms/js-echobot-sample) sample. +#### The turn context -### package.json +The _turn context_ object provides information about the activity such as the sender and receiver, the channel, and other data needed to process the activity. It also allows for the addition of information during the turn across various layers of the bot. -**package.json** specifies dependencies and their associated versions for your bot. This is all set up by the template and your system. +The turn context is one of the most important abstractions in the SDK. Not only does it carry the inbound activity to all the middleware components and the application logic but it also provides the mechanism whereby the middleware components and the bot logic can send outbound activities. -### .env file +#### Middleware -The **.env** file specifies the configuration information for your bot, such as the port number, app ID, and password among other things. If using certain technologies or using this bot in production, you will need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. +Middleware is much like any other messaging middleware, comprising a linear set of components that are each executed in order, giving each a chance to operate on the activity. The final stage of the middleware pipeline is a callback to the turn handler on the bot class the application has registered with the adapter's _process activity_ method. Middleware implements an _on turn_ method which the adapter calls. -To use the **.env** configuration file, the template needs an extra package included. First, get the `dotenv` package from npm: +The turn handler takes a turn context as its argument, typically the application logic running inside the turn handler function will process the inbound activity's content and generate one or more activities in response, sending these outbound activities using the _send activity_ function on the turn context. Calling _send activity_ on the turn context will cause the middleware components to be invoked on the outbound activities. Middleware components execute before and after the bot's turn handler function. The execution is inherently nested and, as such, sometimes referred to being like an onion. -`npm install dotenv` +The [middleware](bot-builder-concept-middleware.md) topic describes middleware in greater depth. -# [Python](#tab/python) +### Bot state and storage -### requirements.txt +As with other web apps, a bot is inherently stateless. +State within a bot follows the same paradigms as modern web applications, and the Bot Framework SDK provides storage layer and state management abstractions to make state management easier. -**requirements.txt** specifies dependencies and their associated versions for your bot. This is all setup by the template and your system. +The [managing state](bot-builder-concept-state.md) topic describes these state and storage features. -Dependencies should be installed using `pip install -r requirements.txt` +### Messaging endpoint and provisioning -### config.py +Typically, your application will need a REST endpoint at which to receive messages. It will also need to provision resources for your bot in accordance with the platform you decide to use. -The **config.py** file specifies the configuration information for your bot, such as the port number, app ID, and password among other things. If using certain technologies or using this bot in production, you will need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. +Follow the [Create a bot](../bot-service-quickstart-create-bot.md) quickstart to create and test a simple echo bot. ---- +## HTTP Details -### Bot logic +Activities arrive at the bot from the Bot Framework Service via an HTTP POST request. The bot responds to the inbound POST request with a 200 HTTP status code. Activities sent from the bot to the channel are sent on a separate HTTP POST to the Bot Framework Service. This, in turn, is acknowledged with a 200 HTTP status code. -The bot logic processes incoming activities from one or more channels and generates outgoing activities in response. +The protocol doesn't specify the order in which these POST requests and their acknowledgments are made. However, to fit with common HTTP service frameworks, typically these requests are nested, meaning that the outbound HTTP request is made from the bot within the scope of the inbound HTTP request. This pattern is illustrated in the earlier diagram. Since there are two distinct HTTP connections back to back, the security model must provide for both. -# [C#](#tab/csharp) +> [!NOTE] +> The bot has 15 seconds to acknowledge the call with a status 200 on most channels. If the bot doesn't respond within 15 seconds, an HTTP GatewayTimeout error (504) occurs. -The main bot logic is defined in the bot code, here called `Bots/EchoBot.cs`. `EchoBot` derives from `ActivityHandler`, which in turn derives from the `IBot` interface. `ActivityHandler` defines various handlers for different types of activities, such as the two defined here: `OnMessageActivityAsync`, and `OnMembersAddedAsync`. These methods are protected, but can be overwritten since we're deriving from `ActivityHandler`. +## The activity processing stack -The handlers defined in `ActivityHandler` are: +Let's drill into the previous sequence diagram with a focus on the arrival of a message activity. -| Event | Handler | Description | -| :-- | :-- | :-- | -| Any activity type received | `OnTurnAsync` | Calls one of the other handlers, based on the type of activity received. | -| Message activity received | `OnMessageActivityAsync` | Override this to handle a `message` activity. | -| Conversation update activity received | `OnConversationUpdateActivityAsync` | On a `conversationUpdate` activity, calls a handler if members other than the bot joined or left the conversation. | -| Non-bot members joined the conversation | `OnMembersAddedAsync` | Override this to handle members joining a conversation. | -| Non-bot members left the conversation | `OnMembersRemovedAsync` | Override this to handle members leaving a conversation. | -| Event activity received | `OnEventActivityAsync` | On an `event` activity, calls a handler specific to the event type. | -| Token-response event activity received | `OnTokenResponseEventAsync` | Override this to handle token response events. | -| Non-token-response event activity received | `OnEventAsync` | Override this to handle other types of events. | -| Message reaction activity received | `OnMessageReactionActivityAsync` | On a `messageReaction` activity, calls a handler if one or more reactions were added or removed from a message. | -| Message reactions added to a message | `OnReactionsAddedAsync` | Override this to handle reactions added to a message. | -| Message reactions removed from a message | `OnReactionsRemovedAsync` | Override this to handle reactions removed from a message. | -| Other activity type received | `OnUnrecognizedActivityTypeAsync` | Override this to handle any activity type otherwise unhandled. | +:::image type="complex" source="media/bot-builder-activity-processing-stack.png" alt-text="Sequence diagram illustrating how an activity is processed by a bot."::: + The channel sends the user's message to the Azure AI Bot Service, and the service forwards the message to the bot's messaging endpoint. The bot's response is sent to the user within the scope of the turn. +:::image-end::: -These different handlers have a `turnContext` that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, `OnMessageActivityAsync` will always be handled, and is generally the most common. +In the example above, the bot replied to the message activity with another message activity containing the same text message. Processing starts with the HTTP POST request, with the activity information carried as a JSON payload, arriving at the web server. Often, ASP.NET projects are used for C# bots, and a popular framework such as Express or restify is used for JavaScript Node.js bots. -As in previous 4.x versions of this framework, there is also the option to implement the public method `OnTurnAsync`. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of `OnTurnAsync`, it is still an option. +The _adapter_, an integrated component of the SDK, is the core of the SDK runtime. The activity is carried as JSON in the HTTP POST body. This JSON is deserialized to create the _activity_ object that is then handed to the adapter through its _process activity_ method. On receiving the activity, the adapter creates a _turn context_ and calls the middleware. -> [!IMPORTANT] -> If you do override the `OnTurnAsync` method, you'll need to call `base.OnTurnAsync` to get the base implementation to call all the other `OnAsync` handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run. - -In this sample, we welcome a new user or echo back the message the user sent using the `SendActivityAsync` call. The outbound activity corresponds to the outbound HTTP POST request. - -```cs -public class MyBot : ActivityHandler -{ - protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken); - } - - protected override async Task OnMembersAddedAsync(IList membersAdded, ITurnContext turnContext, CancellationToken cancellationToken) - { - foreach (var member in membersAdded) - { - await turnContext.SendActivityAsync(MessageFactory.Text($"welcome {member.Name}"), cancellationToken); - } - } -} -``` - -# [JavaScript](#tab/javascript) - -The main bot logic is defined in the bot code, here called `bots\echoBot.js`. `EchoBot` derives from `ActivityHandler`. `ActivityHandler` defines various events for different types of activities, and you can modify your bot's behavior by registering event listeners, such as with `onMessage` and `onConversationUpdate` here. - -Use these methods to register listeners for each type of event: - -| Event | Registration method | Description | -| :-- | :-- | :-- | -| Any activity type received | `onTurn` | Registers a listener for when any activity is received. | -| Message activity received | `onMessage` | Registers a listener for when a `message` activity is received. | -| Conversation update activity received | `onConversationUpdate` | Registers a listener for when any `conversationUpdate` activity is received. | -| Members joined the conversation | `onMembersAdded` | Registers a listener for when members joined the conversation, including the bot. | -| Members left the conversation | `onMembersRemoved` | Registers a listener for when members left the conversation, including the bot. | -| Message reaction activity received | `onMessageReaction` | Registers a listener for when any `messageReaction` activity is received. | -| Message reactions added to a message | `onReactionsAdded` | Registers a listener for when reactions are added to a message. | -| Message reactions removed from a message | `onReactionsRemoved` | Registers a listener for when reactions are removed from a message. | -| Event activity received | `onEvent` | Registers a listener for when any `event` activity is received. | -| Token-response event activity received | `onTokenResponseEvent` | Registers a listener for when a `tokens/response` event is received. | -| Other activity type received | `onUnrecognizedActivityType` | Registers a listener for when a handler for the specific type of activity is not defined. | -| Activity handlers have completed | `onDialog` | Called after any applicable handlers have completed. | - -Call the `next` continuation function from each handler to allow processing to continue. If `next` is not called, processing of the activity ends. - -For example, this bot registers listeners for messages and conversation updates. When it receives a message from the user, it echoes back the message they sent. - -```javascript -const { ActivityHandler } = require('botbuilder'); - -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) => { - await context.sendActivity(`You said '${ context.activity.text }'`); - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - this.onConversationUpdate(async (context, next) => { - await context.sendActivity('[conversationUpdate event detected]'); - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} - -module.exports.MyBot = MyBot; -``` - -# [Python](#tab/python) - -The main bot logic is defined in the bot code, here called `bots/echo_bot.py`. `EchoBot` derives from `ActivityHandler`, which in turn derives from the `Bot` interface. `ActivityHandler` defines various handlers for different types of activities, such as the two defined here: `on_message_activity`, and `on_members_added`. These methods are protected, but can be overwritten since we're deriving from `ActivityHandler`. - -The handlers defined in `ActivityHandler` are: - -| Event | Handler | Description | -| :-- | :-- | :-- | -| Any activity type received | `on_turn` | Calls one of the other handlers, based on the type of activity received. | -| Message activity received | `on_message_activity` | Override this to handle a `message` activity. | -| Conversation update activity received | `on_conversation_update_activity` | On a `conversationUpdate` activity, calls a handler if members other than the bot joined or left the conversation. | -| Non-bot members joined the conversation | `on_members_added_activity` | Override this to handle members joining a conversation. | -| Non-bot members left the conversation | `on_members_removed_activity` | Override this to handle members leaving a conversation. | -| Event activity received | `on_event_activity` | On an `event` activity, calls a handler specific to the event type. | -| Token-response event activity received | `on_token_response_event` | Override this to handle token response events. | -| Non-token-response event activity received | `on_event_activity` | Override this to handle other types of events. | -| Message reaction activity received | `on_message_reaction_activity` | On a `messageReaction` activity, calls a handler if one or more reactions were added or removed from a message. | -| Message reactions added to a message | `on_reactions_added` | Override this to handle reactions added to a message. | -| Message reactions removed from a message | `on_reactions_removed` | Override this to handle reactions removed from a message. | -| Other activity type received | `on_unrecognized_activity_type` | Override this to handle any activity type otherwise unhandled. | - -These different handlers have a `turn_context` that provides information about the incoming activity, which corresponds to the inbound HTTP request. Activities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, `on_message_activity` will always be handled, and is generally the most common. - -As in previous 4.x versions of this framework, there is also the option to implement the public method `on_turn`. Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of `on_turn`, it is still an option. +As mentioned above, the turn context provides the mechanism for the bot to send outbound activities, most often in response to an inbound activity. The turn context provides _send_, _update_, and _delete activity_ response methods. Each response method runs in an asynchronous process. > [!IMPORTANT] -> If you do override the `on_turn` method, you'll need to call `super().on_turn` to get the base implementation to call all the other `on_` handlers or call those handlers yourself. Otherwise, those handlers won't be called and that code won't be run. +> The thread handling the primary bot turn deals with disposing of the context object when it's done. **Be sure to `await` any activity calls** so the primary thread will wait on the generated activity before finishing its processing and disposing of the turn context. Otherwise, if a response (including its handlers) takes any significant amount of time and tries to act on the context object, it may get a _context was disposed_ error. -In this sample, we welcome a new user or echo back the message the user sent using the `send_activity` call. The outbound activity corresponds to the outbound HTTP POST request. +## Bot templates -```py -class MyBot(ActivityHandler): - async def on_members_added_activity( - self, members_added: [ChannelAccount], turn_context: TurnContext - ): - for member in members_added: - if member.id != turn_context.activity.recipient.id: - await turn_context.send_activity("Hello and welcome!") +You need to choose the application layer use for your app; however, the Bot Framework has templates and samples for ASP.NET (C#), restify (JavaScript), and aiohttp (Python). The documentation is written assuming you use one of these platforms, but the SDK doesn't require it of you. See the [Create a bot](../bot-service-quickstart-create-bot.md) quickstart for instructions on how to access and install the templates. - async def on_message_activity(self, turn_context: TurnContext): - return await turn_context.send_activity( - f"Echo: {turn_context.activity.text}" - ) -``` +A bot is a web application, and templates are provided for each language version of the SDK. +All templates provide a default endpoint implementation and adapter. +Each template includes: ---- +- Resource provisioning +- A language-specific HTTP endpoint implementation that routes incoming activities to an adapter. +- An adapter object +- A bot object -### Access the bot from your app +The main difference between the different template types is in the bot object. +The templates are: -# [C#](#tab/csharp) +- **Empty bot** + - Includes an activity handler that welcomes a user to the conversation by sending a "hello world" message on the first turn of the conversation. +- **Echo bot** + - Uses an activity handler to welcome users and echo back user input. +- **Core bot** + - Brings together many features of the SDK and demonstrates best practices for a bot. + - Uses an activity handler to welcome users. + - Uses a component dialog and child dialogs to manage the conversation. + - The dialogs use Language Understanding (LUIS) and QnA Maker features. -#### Set up services +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] -The `ConfigureServices` method in the `Startup.cs` file loads the connected services, as well as their keys from `appsettings.json` or Azure Key Vault (if there are any), connects state, and so on. Here, we're adding MVC and setting the compatibility version on our services, then setting up the adapter and bot to be available through dependency injection to the bot controller. +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] - +## Additional information -```csharp -// This method gets called by the runtime. Use this method to add services to the container. -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); +### Managing bot resources - // Create the credential provider to be used with the Bot Framework Adapter. - services.AddSingleton(); +You'll need to manage the resources for your bot, such as its app ID and password, and also information for any connected services. When you deploy your bot, it will need secure access to this information. To avoid complexity, most of the Bot Framework SDK articles don't describe how to manage this information. - // Create the Bot Framework Adapter. - services.AddSingleton(); +- For general security information, see [Bot Framework security guidelines](bot-builder-security-guidelines.md). +- To manage keys and secrets in Azure, see [About Azure Key Vault](/azure/key-vault/general/overview). - // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. - services.AddTransient(); -} -``` +### Channel adapters -The `Configure` method finishes the configuration of your app by specifying that the app use MVC and a few other files. All bots using the Bot Framework will need that configuration call, however that will already be defined in samples or the VSIX template when you build your bot. `ConfigureServices` and `Configure` are called by the runtime when the app starts. +The SDK also lets you use channel adapters, in which the adapter itself additionally performs the tasks that the Bot Connector Service would normal do for a channel. -#### Bot Controller +The SDK provides a few channel adapters in some languages. +More channel adapters are available through the Botkit and Community repositories. +For more information, see the Bot Framework SDK repository's table of [channels and adapters](https://github.com/microsoft/botframework-sdk#channels-and-adapters). -The controller, following the standard MVC structure, lets you determine the routing of messages and HTTP POST requests. For our bot, we pass the incoming request on to the adapter's *process async activity* method as explained in the [activity processing stack](#the-activity-processing-stack) section above. In that call, we specify the bot and any other authorization information that may be required. +### The Bot Connector REST API -The controller implements `ControllerBase`, holds the adapter and bot that we set in `Startup.cs` (that are available here through dependency injection), and passes the necessary information on to the bot when it receives an incoming HTTP POST. +The Bot Framework SDK wraps and builds upon the Bot Connector REST API. If you want to understand the underlying HTTP requests that support the SDK, see the Connector [authentication](../rest-api/bot-framework-rest-connector-authentication.md) and associated articles. +The activities a bot sends and receives conform to the [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md). -Here, you'll see the class proceeded by route and controller attributes. These assist the framework to route the messages appropriately and know which controller to use. If you change the value in the route attribute, that changes the endpoint the emulator or other channels use access your bot. - -```cs -// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot -// implementation at runtime. Multiple different IBot implementations running at different endpoints can be -// achieved by specifying a more specific type for the bot constructor argument. -[Route("api/messages")] -[ApiController] -public class BotController : ControllerBase -{ - private readonly IBotFrameworkHttpAdapter Adapter; - private readonly IBot Bot; - - public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) - { - Adapter = adapter; - Bot = bot; - } - - [HttpPost] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await Adapter.ProcessAsync(Request, Response, Bot); - } -} -``` - -# [JavaScript](#tab/javascript) - -#### index.js - -The `index.js` sets up your bot and the hosting service that will forward activities to your bot logic. - -#### Required libraries - -At the very top of your `index.js` file you will find a series of modules or libraries that are being required. These modules will give you access to a set of functions that you may want to include in your application. - -```javascript -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 }); -``` - -#### Set up services - -The next parts set up the server and adapter that allow your bot to communicate with the user and send responses. The server will listen on the specified port from the configuration file, or fall back to _3978_ for connection with your emulator. The adapter will act as the conductor for your bot, directing incoming and outgoing communication, authentication, and so on. - -```javascript -// 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/bot-framework-www-portal-emulator`); - console.log(`\nTo talk to your bot, open the emulator select "Open Bot"`); -}); - -// 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(); -``` - -#### Forwarding requests to the bot logic - -The adapter's `processActivity` sends incoming activities to the bot logic. -The third parameter within `processActivity` is a function handler that will be called to perform the bot's logic after the received [activity](#the-activity-processing-stack) has been pre-processed by the adapter and routed through any middleware. The turn context variable, passed as an argument to the function handler, can be used to provide information about the incoming activity, the sender and receiver, the channel, the conversation, etc. Activity processing is routed to the bot's `run` method. `run` is defined in `ActivityHandler`; it performs some error checking, and then calls the bot's event handlers based on the type of activity received. - -```javascript -// Listen for incoming requests. -server.post('/api/messages', (req, res) => { - adapter.processActivity(req, res, async (context) => { - // Route to main dialog. - await myBot.run(context); - }); -}); -``` - -# [Python](#tab/python) - -#### app.py - -The `app.py` sets up your bot and the hosting service that will forward activities to your bot logic. - -#### Required libraries - -At the very top of your `app.py` file you will find a series of modules or libraries that are being required. These modules will give you access to a set of functions that you may want to include in your application. - -```py -from botbuilder.core import BotFrameworkAdapterSettings, TurnContext, BotFrameworkAdapter -from botbuilder.schema import Activity, ActivityTypes - -from bots import MyBot - -# Create the loop and Flask app -LOOP = asyncio.get_event_loop() -app = Flask(__name__, instance_relative_config=True) -app.config.from_object("config.DefaultConfig") -``` - -#### Set up services - -The next parts set up the server and adapter that allow your bot to communicate with the user and send responses. The server will listen on the specified port from the configuration file, or fall back to _3978_ for connection with your emulator. The adapter will act as the conductor for your bot, directing incoming and outgoing communication, authentication, and so on. - -```py -# Create adapter. -# See https://aka.ms/about-bot-adapter to learn more about how bots work. -SETTINGS = BotFrameworkAdapterSettings(app.config["APP_ID"], app.config["APP_PASSWORD"]) -ADAPTER = BotFrameworkAdapter(SETTINGS) - -# Catch-all for errors. -async def on_error(context: TurnContext, error: Exception): - # This check writes out errors to console log .vs. app insights. - # NOTE: In production environment, you should consider logging this to Azure - # application insights. - print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) - - # Send a message to the user - await context.send_activity("The bot encountered an error or bug.") - await context.send_activity("To continue to run this bot, please fix the bot source code.") - # Send a trace activity if we're talking to the Bot Framework Emulator - if context.activity.channel_id == 'emulator': - # Create a trace activity that contains the error object - trace_activity = Activity( - label="TurnError", - name="on_turn_error Trace", - timestamp=datetime.utcnow(), - type=ActivityTypes.trace, - value=f"{error}", - value_type="https://www.botframework.com/schemas/error" - ) - # Send a trace activity, which will be displayed in Bot Framework Emulator - await context.send_activity(trace_activity) - -ADAPTER.on_turn_error = on_error - -# Create the Bot -BOT = MyBot() -``` - -#### Forwarding requests to the bot logic - -The adapter's `process_activity` sends incoming activities to the bot logic. -The third parameter within `process_activity` is a function handler that will be called to perform the bot's logic after the received [activity](#the-activity-processing-stack) has been pre-processed by the adapter and routed through any middleware. The turn context variable, passed as an argument to the function handler, can be used to provide information about the incoming activity, the sender and receiver, the channel, the conversation, etc. Activity processing is routed to the bot's `on_turn` method. `on_turn` is defined in `ActivityHandler`; it performs some error checking, and then calls the bot's event handlers based on the type of activity received. - -```py -# Listen for incoming requests on /api/messages -@app.route("/api/messages", methods=["POST"]) -def messages(): - # Main bot message handler. - if "application/json" in request.headers["Content-Type"]: - body = request.json - else: - return Response(status=415) - - activity = Activity().deserialize(body) - auth_header = ( - request.headers["Authorization"] if "Authorization" in request.headers else "" - ) - - try: - task = LOOP.create_task( - ADAPTER.process_activity(activity, auth_header, BOT.on_turn) - ) - LOOP.run_until_complete(task) - return Response(status=201) - except Exception as exception: - raise exception -``` - ---- - -## Manage bot resources - -The bot resources, such as app ID, passwords, keys or secrets for connected services, will need to be managed appropriately. For more on how to do so, see [Manage bot resources](bot-file-basics.md). - -## Additional resources +## Next steps - To understand the role of state in bots, see [managing state](bot-builder-concept-state.md). - - To understand key concepts of developing bots for Microsoft Teams, see [How Microsoft Teams bots work](bot-builder-basics-teams.md) diff --git a/articles/v4sdk/bot-builder-channeldata.md b/articles/v4sdk/bot-builder-channeldata.md index 834ee98cc..27e5a8d95 100644 --- a/articles/v4sdk/bot-builder-channeldata.md +++ b/articles/v4sdk/bot-builder-channeldata.md @@ -1,18 +1,511 @@ --- -title: Implement channel-specific functionality - Bot Service -description: Learn how to implement channel-specific functionality using the Bot Framework SDK for .NET. -keywords: channel specific, email, slack, facebook, telegram, kik, custom channel -author: RobStand -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +title: Implement channel-specific functionality in Bot Framework SDK +description: Learn how to implement channel-specific functionality using the Bot Framework SDK for .NET. You can do so by passing native metadata to a channel. +keywords: channel specific, email, slack, facebook, telegram, custom channel +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Implement channel-specific functionality -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -[!INCLUDE [Channel Data Content](../includes/snippet-channeldata.md)] +Some channels provide features that can't be implemented with only message text and attachments. To implement channel-specific functionality, you can pass native metadata to a channel in the activity object's _channel data_ property. For example, your bot can use the channel data property to instruct Telegram to send a sticker or to instruct Office365 to send an email. + +This article describes how to use a message activity's channel data property to implement this channel-specific functionality: + +| Channel | Functionality | +|--|--| +| [Email](#create-a-custom-email-message) | Send and receive an email that contains body, subject, and importance metadata. | +| [Facebook](#create-a-facebook-notification) | Send Facebook notifications natively. | +| [LINE](#create-a-line-message) | Send a message that implements LINE-specific message types. | +| [Slack](#create-a-full-fidelity-slack-message) | Send full fidelity Slack messages. | +| [Teams](#add-a-bot-to-teams) | Handle @-mentions in Microsoft Teams messages. | +| [Telegram](#create-a-telegram-message) | Perform Telegram-specific actions, such as sharing a voice memo or a sticker. | + +> [!NOTE] +> The value of an activity object's channel data property is a JSON object. +> Therefore, the examples in this article show the expected format of the +> `channelData` JSON property in various scenarios. +> To create a JSON object using .NET, use the `JObject` (.NET) class. + +## Create a custom email message + +To create a custom email message, set the activity `channelData` property to a JSON object that contains the following properties: + +| Property | Description | +|:-|:-| +| bccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Bcc (blind carbon copy) field. | +| ccRecipients | A semicolon (;) delimited string of email addresses to add to the message's Cc (carbon copy) field. | +| htmlBody | An HTML document that specifies the body of the email message. See the channel's documentation for information about supported HTML elements and attributes. | +| importance | The email's importance level. Valid values are **high**, **normal**, and **low**. The default value is **normal**. | +| toRecipients | A semicolon (;) delimited string of email addresses to add to the message's To field. | + +The outgoing and incoming messages between the user and the bot may have a `channelData` activity that contains a JSON object whose properties are specified in the previous table. +The snippet below shows an example of the `channelData` property for an incoming custom email message, from the bot to the user. + +[!INCLUDE [email channelData json](../includes/snippet-channelData-email.md)] + +## Create a Facebook notification + +To create a Facebook notification, +set the activity object's channel data property to a JSON object that specifies these properties: + +| Property | Description | +|--|--| +| notification_type | The type of notification, such as **REGULAR**, **SILENT_PUSH**, or **NO_PUSH**. | +| attachment | An attachment that specifies an image, video, or other multimedia type, or a templated attachment such as a receipt. | + +> [!NOTE] +> For details about format and contents of the `notification_type` property and `attachment` property, see the +> [Facebook API documentation](https://developers.facebook.com/docs/messenger-platform/send-api-reference#guidelines). + +This snippet shows an example of the `channelData` property for a Facebook receipt attachment. + +```json +"channelData": { + "notification_type": "NO_PUSH", + "attachment": { + "type": "template" + "payload": { + "template_type": "receipt", + //... + } + } +} +``` + +## Create a LINE message + +To create a message that implements LINE-specific message types (such as sticker, templates, or LINE-specific action types like opening the phone camera), set the activity object's channel data property to a JSON object that specifies LINE message types and action types. + +| Property | Description | +| -------- | --------------------------------- | +| type | The LINE action/message type name | + +These LINE message types are supported: + +- Sticker +- Imagemap +- Template (Button, confirm, carousel) +- Flex + +These LINE actions can be specified in the action field of the message type JSON object: + +- Postback +- Message +- URI +- Datetimerpicker +- Camera +- Camera roll +- Location + +For details about these LINE methods and their parameters, see the [LINE Bot API documentation](https://developers.line.biz/en/docs/messaging-api/). + +This snippet shows an example of a `channelData` property that specifies a channel message type `ButtonTemplate` and three action types: "camera", "cameraRoll", and "datetimepicker". + +```json +"channelData": { + "type": "template", + "altText": "This is a buttons template", + "template": { + "type": "buttons", + "thumbnailImageUrl": "https://example.com/bot/images/image.jpg", + "imageAspectRatio": "rectangle", + "imageSize": "cover", + "imageBackgroundColor": "#FFFFFF", + "title": "Menu", + "text": "Please select", + "defaultAction": { + "type": "uri", + "label": "View detail", + "uri": "http://example.com/page/123" + }, + "actions": [{ + "type": "cameraRoll", + "label": "Camera roll" + }, + { + "type": "camera", + "label": "Camera" + }, + { + "type": "datetimepicker", + "label": "Select date", + "data": "storeId=12345", + "mode": "datetime", + "initial": "2017-12-25t00:00", + "max": "2018-01-24t23:59", + "min": "2017-12-25t00:00" + } + ] + } +} +``` + +## Create a full-fidelity Slack message + +To create a full-fidelity Slack message, set the activity object's channel data property to a JSON object that specifies: + +- [Slack messages](https://api.slack.com/docs/messages) +- [Slack attachments](https://api.slack.com/docs/message-attachments) +- [Slack buttons](https://api.slack.com/docs/message-buttons) + +> [!NOTE] +> To support buttons in Slack messages, you must enable **Interactive Messages** when you +> [connect your bot](../bot-service-manage-channels.md) to the Slack channel. + +This snippet shows an example of the `channelData` property for a custom Slack message. + +```json +"channelData": { + "text": "Now back in stock! :tada:", + "attachments": [ + { + "title": "The Further Adventures of Slackbot", + "author_name": "Stanford S. Strickland", + "author_icon": "https://api.slack.com/img/api/homepage_custom_integrations-2x.png", + "image_url": "http://i.imgur.com/OJkaVOI.jpg?1" + }, + { + "fields": [ + { + "title": "Volume", + "value": "1", + "short": true + }, + { + "title": "Issue", + "value": "3", + "short": true + } + ] + }, + { + "title": "Synopsis", + "text": "After @episod pushed exciting changes to a devious new branch back in Issue 1, Slackbot notifies @don about an unexpected deploy..." + }, + { + "fallback": "Would you recommend it to customers?", + "title": "Would you recommend it to customers?", + "callback_id": "comic_1234_xyz", + "color": "#3AA3E3", + "attachment_type": "default", + "actions": [ + { + "name": "recommend", + "text": "Recommend", + "type": "button", + "value": "recommend" + }, + { + "name": "no", + "text": "No", + "type": "button", + "value": "bad" + } + ] + } + ] +} +``` + +When a user clicks a button within a Slack message, your bot will receive a response message in which the channel data property is populated with a `payload` JSON object. The `payload` object specifies contents of the original message, identifies the button that was clicked, and identifies the user who clicked the button. + +This snippet shows an example of the `channelData` property in the message that a bot receives when a user clicks a button in the Slack message. + +```json +"channelData": { + "payload": { + "actions": [ + { + "name": "recommend", + "value": "yes" + } + ], + //... + "original_message": "{...}", + "response_url": "https://hooks.slack.com/actions/..." + } +} +``` + +Your bot can reply to this message in the normal manner, or it can post its response directly to the endpoint that is specified by the `payload` object's `response_url` property. For information about when and how to post a response to the `response_url`, see [Slack Buttons](https://api.slack.com/docs/message-buttons). + +You can create dynamic buttons using the following JSON: + +```json +{ + "text": "Would you like to play a game ? ", + "attachments": [ + { + "text": "Choose a game to play!", + "fallback": "You are unable to choose a game", + "callback_id": "wopr_game", + "color": "#3AA3E3", + "attachment_type": "default", + "actions": [ + { + "name": "game", + "text": "Chess", + "type": "button", + "value": "chess" + }, + { + "name": "game", + "text": "Falken's Maze", + "type": "button", + "value": "maze" + }, + { + "name": "game", + "text": "Thermonuclear War", + "style": "danger", + "type": "button", + "value": "war", + "confirm": { + "title": "Are you sure?", + "text": "Wouldn't you prefer a good game of chess?", + "ok_text": "Yes", + "dismiss_text": "No" + } + } + ] + } + ] +} +``` + +To create interactive menus, use the following JSON: + +```json +{ + "text": "Would you like to play a game ? ", + "response_type": "in_channel", + "attachments": [ + { + "text": "Choose a game to play", + "fallback": "If you could read this message, you'd be choosing something fun to do right now.", + "color": "#3AA3E3", + "attachment_type": "default", + "callback_id": "game_selection", + "actions": [ + { + "name": "games_list", + "text": "Pick a game...", + "type": "select", + "options": [ + { + "text": "Hearts", + "value": "menu_id_hearts" + }, + { + "text": "Bridge", + "value": "menu_id_bridge" + }, + { + "text": "Checkers", + "value": "menu_id_checkers" + }, + { + "text": "Chess", + "value": "menu_id_chess" + }, + { + "text": "Poker", + "value": "menu_id_poker" + }, + { + "text": "Falken's Maze", + "value": "menu_id_maze" + }, + { + "text": "Global Thermonuclear War", + "value": "menu_id_war" + } + ] + } + ] + } + ] +} +``` + +## Add a bot to Teams + +Bots added to a team become another team member, who can be `@mentioned` as part of the conversation. In fact, bots only receive messages when they're `@mentioned`, so other conversations on the channel are not sent to the bot. For more information, see [Channel and Group chat conversations with a Microsoft Teams bot](/microsoftteams/platform/concepts/bots/bot-conversations/bots-conv-channel). + +Because bots in a group or channel respond only when they're mentioned (`@botname`) in a message, every message received by a bot in a group channel contains its own name, and you must ensure your message parsing handles that. In addition, bots can parse out other users mentioned and mention users as part of their messages. + +### Check for and strip @bot mention + +```csharp +Mention[] m = sourceMessage.GetMentions(); +var messageText = sourceMessage.Text; + +for (int i = 0;i < m.Length;i++) +{ + if (m[i].Mentioned.Id == sourceMessage.Recipient.Id) + { + //Bot is in the @mention list. + //The below example will strip the bot name out of the message, so you can parse it as if it wasn't included. Note that the Text object will contain the full bot name, if applicable. + if (m[i].Text != null) + messageText = messageText.Replace(m[i].Text, ""); + } +} +``` + +```javascript +var text = message.text; +if (message.entities) { + message.entities + .filter(entity => ((entity.type === "mention") && (entity.mentioned.id.toLowerCase() === botId))) + .forEach(entity => { + text = text.replace(entity.text, ""); + }); + text = text.trim(); +} + +``` + +> [!IMPORTANT] +> Adding a bot by GUID, for anything other than testing purposes, isn't recommended. Doing so severely limits the functionality of a bot. Bots in production should be added to Teams as part of an app. See [Create a bot](/microsoftteams/platform/concepts/bots/bots-create) and [Test and debug your Microsoft Teams bot](/microsoftteams/platform/concepts/bots/bots-test). + +## Create a Telegram message + +To create a message that implements Telegram-specific actions, such as sharing a voice memo or a sticker, set the activity object's channel data property to a JSON object that specifies these properties: + +| Property | Description | +| ---------- | --------------------------------------- | +| method | The Telegram Bot API method to call. | +| parameters | The parameters of the specified method. | + +These Telegram methods are supported: + +- answerInlineQuery +- editMessageCaption +- editMessageReplyMarkup +- editMessageText +- forwardMessage +- banChatMember +- sendAudio +- sendChatAction +- sendContact +- sendDocument +- sendLocation +- sendMessage +- sendPhoto +- sendSticker +- sendVenue +- sendVideo +- sendVoice +- unbanChatMember + +For details about these Telegram methods and their parameters, see the [Telegram Bot API documentation](https://core.telegram.org/bots/api#available-methods). + +> [!NOTE] +> +> - The `chat_id` parameter is common to all Telegram methods. If you don't specify `chat_id` as a parameter, the framework will provide the ID for you. +> - Instead of passing file contents inline, specify the file using a URL and media type as shown in the example below. +> - Within each message that your bot receives from the Telegram channel, the `ChannelData` property will include the message that your bot sent previously. + +This snippet shows an example of a `channelData` property that specifies a single Telegram method: + +```json +"channelData": { + "method": "sendSticker", + "parameters": { + "sticker": { + "url": "https://domain.com/path/gif", + "mediaType": "image/gif", + } + } +} +``` + +This snippet shows an example of a `channelData` property that specifies an array of Telegram methods: + +```json +"channelData": [ + { + "method": "sendSticker", + "parameters": { + "sticker": { + "url": "https://domain.com/path/gif", + "mediaType": "image/gif", + } + } + }, + { + "method": "sendMessage", + "parameters": { + "text": "This message is HTML formatted.", + "parse_mode": "HTML" + } + } +] +``` + +When a Telegram method is implemented, your bot will receive a response message in which the channel data property is populated with a JSON object. This response object specifies the contents of the original message, including an `update_id` and, at most, one optional parameter. For information about receiving incoming responses, see [Getting updates](https://core.telegram.org/bots/api#getting-updates). + +This snippet shows an example of the `channelData` property in the message that a bot receives when a poll is created: + +```json +"channelData": { + "update_id": 43517575, + "message": { + "message_id": 618, + "from": { + "id": 803613355, + "is_bot": false, + "first_name": "Joe", + "last_name": "Doe", + "username": "jdoe", + "language_code": "en" + }, + "chat": { + "id": 803613355, + "first_name": "Joe", + "last_name": "Doe", + "username": "jdoe", + "type": "private" + }, + "date": 1582577834, + "poll": { + "id": "5089525250643722242", + "question": "How to win?", + "options": [ + { + "text": "Be the best", + "voter_count": 0 + }, + { + "text": "Help those in need", + "voter_count": 0 + }, + { + "text": "All of the above", + "voter_count": 0 + } + ], + "total_voter_count": 0, + "is_closed": false, + "is_anonymous": true, + "type": "regular", + "allows_multiple_answers": false + } + } +} +``` + +## Additional resources + +- [Entities and activity types](../bot-service-activities-entities.md) +- [Bot Framework Activity schema](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md) diff --git a/articles/v4sdk/bot-builder-compositcontrol.md b/articles/v4sdk/bot-builder-compositcontrol.md index 93048fe02..85b340c33 100644 --- a/articles/v4sdk/bot-builder-compositcontrol.md +++ b/articles/v4sdk/bot-builder-compositcontrol.md @@ -1,48 +1,56 @@ --- -title: Reuse dialogs - Bot Service -description: Learn how to modularize your bot logic using component dialogs in the Bot Framework SDK. +title: Manage dialog complexity +description: Learn how to modularize your dialog complexity using component dialogs in the Bot Framework SDK. keywords: composite control, modular bot logic -author: v-ducvo -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/30/2020 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- -# Reuse dialogs +# Manage dialog complexity -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -With component dialogs, you can create independent dialogs to handle specific scenarios, breaking a large dialog set into more manageable pieces. Each of these pieces has its own dialog set, and avoids any name collisions with the dialog sets outside of it. +With component dialogs, you can create independent dialogs to handle specific scenarios, breaking a large dialog set into more manageable pieces. Each of these pieces has its own dialog set, and avoids any name collisions with the dialog sets outside of it. Component dialogs are reusable in that they can be: + +- Added to another `ComponentDialog` or `DialogSet` in your bot. +- Exported as a part of a package. +- Used within other bots. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Knowledge of [bot basics][concept-basics], the [dialogs library][concept-dialogs], and how to [manage conversations][simple-flow]. -- A copy of the multi-turn prompt sample in [**C#**][cs-sample], [**JavaScript**][js-sample], or [**Python**][python-sample]. +- A copy of the multi-turn prompt sample in [**C#**][cs-sample], [**JavaScript**][js-sample], [**Java**][java-sample], or [**Python**][python-sample]. ## About the sample -In the multi-turn prompt sample, we use a waterfall dialog, a few prompts, and a component dialog to create a simple interaction that asks the user a series of questions. The code uses a dialog to cycle through these steps: +In the multi-turn prompt sample, we use a waterfall dialog, a few prompts, and a component dialog to create an interaction that asks the user a series of questions. The code uses a dialog to cycle through these steps: -| Steps | Prompt type | -|:-------------|:-------------| -| Ask the user for their mode of transportation | Choice prompt | -| Ask the user for their name | Text prompt | -| Ask the user if they want to provide their age | Confirm prompt | -| If they answered yes, asks for their age | Number prompt with validation to only accept ages greater than 0 and less than 150. | -| Asks if the collected information is "ok" | Reuse Confirm prompt | +| Steps | Prompt type | +|:-----------------------------------------------|:------------------------------------------------------------------------------------| +| Ask the user for their mode of transportation | Choice prompt | +| Ask the user for their name | Text prompt | +| Ask the user if they want to provide their age | Confirm prompt | +| If they answered yes, ask for their age | Number prompt with validation to only accept ages greater than 0 and less than 150. | +| Ask if the collected information is "ok" | Reuse Confirm prompt | -Finally, if they answered yes, display the collected information; otherwise, tell the user that their information will not be kept. +Finally, if they answered yes, display the collected information; otherwise, tell the user that their information won't be kept. -## Implement the component dialog +## Implement your component dialog -In the multi-turn prompt sample, we use a _waterfall dialog_, a few _prompts_, and a _component dialog_ to create a simple interaction that asks the user a series of questions. +In the multi-turn prompt sample, we use a _waterfall dialog_, a few _prompts_, and a _component dialog_ to create an interaction that asks the user a series of questions. A component dialog encapsulates one or more dialogs. The component dialog has an inner dialog set, and the dialogs and prompts that you add to this inner dialog set have their own IDs, visible only from within the component dialog. -# [C#](#tab/csharp) +### [C#](#tab/csharp) To use dialogs, install the **Microsoft.Bot.Builder.Dialogs** NuGet package. @@ -50,19 +58,19 @@ To use dialogs, install the **Microsoft.Bot.Builder.Dialogs** NuGet package. Here the `UserProfileDialog` class derives from the `ComponentDialog` class. -[!code-csharp[Class](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=17)] +[!code-csharp[Class](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=16)] -Within the constructor, the `AddDialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog, but you can change this by explicitly setting the `InitialDialogId` property. When you start a component dialog, it will start its _initial dialog_. +Within the constructor, the `AddDialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the `InitialDialogId` property. When you start a component dialog, it will start its _initial dialog_. -[!code-csharp[Constructor](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=21-48)] +[!code-csharp[Constructor](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=20-47)] -This is the implementation of the first step of the waterfall dialog. +The following code represents the first step of the waterfall dialog. -[!code-csharp[First step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=50-60)] +[!code-csharp[First step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=61-66)] For more information on implementing waterfall dialogs, see how to [implement sequential conversation flow](bot-builder-dialog-manage-complex-conversation-flow.md). -# [JavaScript](#tab/javascript) +### [JavaScript](#tab/javascript) To use dialogs, your project needs to install the **botbuilder-dialogs** npm package. @@ -72,17 +80,35 @@ Here the `UserProfileDialog` class extends `ComponentDialog`. [!code-javascript[Class](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=28)] -Within the constructor, the `AddDialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog, but you can change this by explicitly setting the `InitialDialogId` property. When you start a component dialog, it will start its _initial dialog_. +Within the constructor, the `AddDialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the `InitialDialogId` property. When you start a component dialog, it will start its _initial dialog_. [!code-javascript[Constructor](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=29-51)] -This is the implementation of the first step of the waterfall dialog. +The following code represents the first step of the waterfall dialog. [!code-javascript[First step](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=70-77)] For more information on implementing waterfall dialogs, see how to [implement sequential conversation flow](bot-builder-dialog-manage-complex-conversation-flow.md). -# [Python](#tab/python) +### [Java](#tab/java) + +**UserProfileDialog.java** + +Here the `UserProfileDialog` class derives from the `ComponentDialog` class. + +[!code-java[Class](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=31)] + +Within the constructor, the `addDialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by calling the `setInitialDialogId` method and provide the name of the initial dialog. When you start a component dialog, it will start its _initial dialog_. + +[!code-java[Constructor](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=34-59)] + +The following code represents the first step of the waterfall dialog. + +[!code-java[First step](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=71-77)] + +For more information on implementing waterfall dialogs, see how to [implement sequential conversation flow](bot-builder-dialog-manage-complex-conversation-flow.md). + +### [Python](#tab/python) To use dialogs, install the **botbuilder-dialogs** and **botbuilder-ai** PyPI packages by running `pip install botbuilder-dialogs` and `pip install botbuilder-ai` from a terminal. @@ -92,11 +118,11 @@ Here the `UserProfileDialog` class extends `ComponentDialog`. [!code-python[Class](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=25)] -Within the constructor, the `add_dialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog, but you can change this by explicitly setting the `initial_dialog_id` property. When you start a component dialog, it will start its _initial dialog_. +Within the constructor, the `add_dialog` method adds dialogs and prompts to the component dialog. The first item you add with this method is set as the initial dialog. You can change the initial dialog by explicitly setting the `initial_dialog_id` property. When you start a component dialog, it will start its _initial dialog_. [!code-python[Constructor](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=25-57)] -This is the implementation of the first step of the waterfall dialog. +The following code represents the first step of the waterfall dialog. [!code-python[First step](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=59-71)] @@ -110,15 +136,15 @@ At run-time, the component dialog maintains its own dialog stack. When the compo - It creates an inner dialog stack that it adds to its state - It starts its initial dialog and adds that to the inner dialog stack. -From the parent context, it will look like the component is the active dialog. From inside the component, it will look like the initial dialog is the active dialog. +The parent context sees the component as the active dialog. However, to the context inside the component, it looks like the initial dialog is the active dialog. -### Implement the rest of the dialog and add it to the bot +## Call the dialog from your bot -In the outer dialog set, the one to which you add the component dialog, the component dialog has the ID that you created it with. In the outer set, the component looks like a single dialog, much like prompts do. +In the outer dialog set, the one to which you added the component dialog, the component dialog has the ID that you created it with. In the outer set, the component looks like a single dialog, much like prompts do. -To use a component dialog, add an instance of it to the bot's dialog set - this is the outer dialog set. +To use a component dialog, add an instance of it to the bot's dialog set. -# [C#](#tab/csharp) +### [C#](#tab/csharp) **Bots\DialogBot.cs** @@ -126,7 +152,7 @@ In the sample, this is done using the `RunAsync` method that is called from the [!code-csharp[OnMessageActivityAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Bots/DialogBot.cs?range=42-48&highlight=6)] -# [JavaScript](#tab/javascript) +### [JavaScript](#tab/javascript) **dialogs/userProfileDialog.js** @@ -140,7 +166,15 @@ The `run` method is called from the bot's `onMessage` method. [!code-javascript[onMessage](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/bots/dialogBot.js?range=24-31&highlight=5)] -# [Python](#tab/python) +### [Java](#tab/java) + +**DialogBot.java** + +In the sample, this is done using the `run` method that is called from the bot's `onMessageActivity` method. + +[!code-java[OnMessageActivity](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/DialogBot.java?range=50-58&highlight=8)] + +### [Python](#tab/python) **helpers/dialog_helper.py** @@ -155,13 +189,13 @@ The `run_dialog` method that is called from the bot's `on_message_activity` meth --- -## To test the bot +## Test your bot -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). 1. Run the sample locally on your machine. -1. Start the emulator, connect to your bot, and send messages as shown below. +1. Start the Emulator, connect to your bot, and send messages as shown below. -![Sample run of the multi-turn prompt dialog](../media/emulator-v4/multi-turn-prompt.png) +:::image type="content" source="../media/emulator-v4/multi-turn-prompt.png" alt-text="Sample transcript from the multi-turn prompt dialog."::: ## Additional information @@ -169,18 +203,14 @@ The `run_dialog` method that is called from the bot's `on_message_activity` meth If you call _cancel all dialogs_ from the component dialog's context, the component dialog will cancel all of the dialogs on its inner stack and then end, returning control to the next dialog on the outer stack. -If you call _cancel all dialogs_ from the outer context, the component is cancelled, along with the rest of the dialogs on the outer context. - -Keep this in mind when managing nested component dialogs in your bot. +If you call _cancel all dialogs_ from the outer context, the component is canceled, along with the rest of the dialogs on the outer context. ## Next steps -You can enhance bots to react to additional input, like "help" or "cancel", that can interrupt the normal flow of the conversation. +Learn how to create complex conversations that branch and loop. > [!div class="nextstepaction"] -> [Handle user interruptions](bot-builder-howto-handle-user-interrupt.md) - - +> [Handle user interruptions](bot-builder-dialog-manage-complex-conversation-flow.md) [concept-basics]: bot-builder-basics.md [concept-state]: bot-builder-concept-state.md @@ -190,6 +220,8 @@ You can enhance bots to react to additional input, like "help" or "cancel", that [prompting]: bot-builder-prompts.md [component-dialogs]: bot-builder-compositcontrol.md -[cs-sample]: https://aka.ms/cs-multi-prompts-sample -[js-sample]: https://aka.ms/js-multi-prompts-sample -[python-sample]: https://aka.ms/python-multi-prompts-sample +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/05.multi-turn-prompt +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/05.multi-turn-prompt +[python-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/05.multi-turn-prompt +[lg-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/language-generation/05.multi-turn-prompt diff --git a/articles/v4sdk/bot-builder-concept-activity-processing.md b/articles/v4sdk/bot-builder-concept-activity-processing.md deleted file mode 100644 index d5a4adcf9..000000000 --- a/articles/v4sdk/bot-builder-concept-activity-processing.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Activity processing - Bot Service -description: Understand activity processing in the bot SDK. -keywords: bot adapter, custom middleware, short circuit, fallback, event handlers -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Activity processing - -[!INCLUDE[applies-to](../includes/applies-to.md)] - -The bot and user interact and exchange information via activities. Each activity received by your bot application is passed to a bot adapter, which passes activity information to your bot logic and ultimately sends any responses to the user. Receiving an activity, and subsequently processing it through your bot, is called a turn; this represents one complete cycle of your bot. A turn ends when all execution is done, the activity is fully processed and all the layers of the bot have completed. - -Activities, particularly those that are [sent from a bot](#generating-responses) during a bot turn, are handled asynchronously. It's a necessary part of building a bot; if you need to brush up on how that all works, check out [async for .NET](https://docs.microsoft.com/dotnet/csharp/async) or [async for JavaScript](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) depending on your language choice. - -## The bot adapter - -The bot adapter encapsulates authentication processes and sends activities to and receives activities from the Bot Connector Service. When your bot receives an activity, the adapter wraps up everything about that activity, creates a [context object](#turn-context) for the turn, passes it to your bot's application logic, and sends responses generated by your bot back to the user's channel. - -## Authentication - -The adapter authenticates each incoming activity the application receives, using information from the activity and the `Authentication` header from the REST request. The adapter uses a connector object and your application's credentials to authenticate the outbound activities to the user. - -Bot Connector Service authentication uses JWT (JSON Web Token) `Bearer` tokens and the **Microsoft app ID** and **Microsoft app password** that Azure creates for you when you create a bot service or register your bot. Your application will need these credentials at initialization time, to allow the adapter to authenticate traffic. - -> [!NOTE] -> If you are running or testing your bot locally, for example, using the Bot Framework Emulator, you can do so without configuring the adapter to authenticate traffic to and from your bot. - -## Turn context - -When an adapter receives an activity, it generates a **turn context** object, which provides information about the incoming activity, the sender and receiver, the channel, the conversation, and other data needed to process the activity. The adapter then passes this context object to the bot. The context object persists for the length of a turn, and provides information on the following: - -* Conversation - Identifies the conversation and includes information about the bot and the user participating in the conversation. -* Activity - The requests and replies in a conversation are all types of activities. This context provides information about the incoming activity, including routing information, information about the channel, the conversation, the sender, and the receiver. -* Custom information – If you extend your bot either by implementing middleware or within your bot logic, you can make additional information available in each turn. - -The context object can also be used to send a response to the user, and get a reference to the adapter. - -> [!NOTE] -> Your application and the adapter will handle requests asynchronously; however, your business logic does not need to be request-response driven. - -## Middleware - -You can add middleware to the adapter. The middleware and the bot logic use the context object to retrieve information about the activity and act accordingly. The middleware and the bot can also update or add information to the context object, such as to track state for a turn, a conversation, or other scope. For more in depth information about middleware, see the [middleware article](~/v4sdk/bot-builder-concept-middleware.md). - -## Generating responses - -The context object provides activity response methods to allow code to respond to an activity: - -* The _send activity_ and _send activities_ methods send one or more activities to the conversation. -* If supported by the channel, the _update activity_ method updates an activity within the conversation. -* If supported by the channel, the _delete activity_ method removes an activity from the conversation. - -Each response method runs in an asynchronous process. When it is called, the activity response method clones the associated [event handler](#response-event-handlers) list before starting to call the handlers, which means it will contain every handler added up to this point but will not contain anything added after the process starts. - -This also means the order of your responses is not guaranteed, particularly when one task is more complex than another. If your bot can generate multiple responses to an incoming activity, make sure that they make sense in whatever order they are received by the user. - -> [!IMPORTANT] -> The thread handling the primary bot turn deals with disposing of the context object when it is done. If a response (including its handlers) take any significant amount of time and try to act on the context object, they may get a `Context was disposed` error. **Be sure to `await` any activity calls** so the primary thread will wait on the generated activity before finishing it's processing and disposing of the turn context. - -## Response event handlers - -In addition to the bot and middleware logic, response handlers (also sometimes referred to as event handlers, or activity event handlers) can be added to the context object. These handlers are called when the associated [response](#generating-responses) happens on the current context object, before executing the actual response. These handlers are useful when you know you'll want to do something, either before or after the actual event, for every activity of that type for the rest of the current response. - -> [!WARNING] -> Be careful to not call an activity response method from within it's respective response event handler, for example, calling the send activity method from within an _on send activity_ handler. Doing so can generate an infinite loop. - -Each new activity gets a new thread to execute on. When the thread to process the activity is created, the list of handlers for that activity is copied to that new thread. No handlers added after that point will be executed for that specific activity event. - -The handlers registered on a context object are handled very similarly to how the adapter manages the [middleware pipeline](~/v4sdk/bot-builder-concept-middleware.md#the-bot-middleware-pipeline). Namely, handlers get called in the order they're added, and calling the _next_ delegate passes control to the next registered event handler. If a handler doesn’t call the next delegate, none of the subsequent event handlers are called, the event [short circuits](~/v4sdk/bot-builder-concept-middleware.md#short-circuiting), and the adapter does not send the response to the channel. - -## Next steps - -> [!div class="nextstepaction"] -> [Middleware](~/v4sdk/bot-builder-concept-middleware.md) diff --git a/articles/v4sdk/bot-builder-concept-adaptive-expressions.md b/articles/v4sdk/bot-builder-concept-adaptive-expressions.md new file mode 100644 index 000000000..bbaf9d618 --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-adaptive-expressions.md @@ -0,0 +1,183 @@ +--- +title: Adaptive expressions in Bot Framework SDK +description: Describes how adaptive expressions work within the Bot Framework SDK. +keywords: adaptive expressions +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: concept-article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Adaptive expressions + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Bots use adaptive expressions to evaluate the outcome of a condition based on runtime information available in memory to the dialog or the [Language Generation](bot-builder-concept-language-generation.md) system. These evaluations determine how your bot reacts to user input and other factors that impact bot functionality. + +Adaptive expressions address this core need by providing an adaptive expression language that can be used with the Bot Framework SDK and other conversational AI components, like [Bot Framework Composer](https://github.com/microsoft/BotFramework-Composer), [Language Generation](bot-builder-concept-language-generation.md), [Adaptive dialogs](bot-builder-adaptive-dialog-Introduction.md), and [Adaptive Cards templating](/adaptive-cards/templating). + +An adaptive expression can contain one or more explicit values, [prebuilt functions](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md), or custom functions. Consumers of adaptive expressions also have the capability to inject additional supported functions. For example, all language generation templates are available as functions as well as additional functions that are only available within that component's use of adaptive expressions. + +## Operators + +Adaptive expressions support the following operator types and expression syntax: + +- arithmetic +- comparison +- logical +- other operators and expressions syntax + +### [Arithmetic](#tab/arithmetic) + +| Operator | Functionality | Prebuilt function equivalent | +|-------------|-------------------------------------------------------------------------------------------|-----------------------------------| +|+ | Addition. Example: A + B |[add][1] | +|- | Subtraction. Example: A - B |[sub][2] | +|unary + | Positive value. Example: +1, +A |N/A | +|unary - | Negative value. Example: -2, -B |N/A | +|\* | Multiplication. Example: A \* B |[mul][3] | +|/ | Division. Example: A / B |[div][4] | +|^ | Exponentiation. Example: A ^ B |[exp][5] | +|% | Modulus. Example: A % B |[mod][6] | + +### [Comparison](#tab/comparison) + +| Operator | Functionality | Prebuilt function equivalent | +|-------------|-------------------------------------------------------------------------------------------|-----------------------------------| +|== | Equals. Example: A == B |[equals][7] | +|\!= | Not equals. Example: A != B |[not][8]([equals][7]()) | +|\> | Greater than. Example: A > B |[greater][9] | +|\< | Less than. Example: A < B |[less][10] | +|\>= | Greater than or equal. Example: A >= B |[greaterOrEquals][11] | +|\<= | Less than or equal. Example: A <= B |[lessOrEquals][12] | + +### [Logical](#tab/logical) + +| Operator | Functionality | Prebuilt function equivalent | +|-------------|-------------------------------------------------------------------------------------------|-----------------------------------| +|&& |And. Example: exp1 && exp2 |[and][13] | +|\|\| |Or. Example: exp1 \|\| exp2 |[or][14] | +|\! |Not. Example: !exp1 |[not][8] | + +### [Other](#tab/other) + +| Operator | Functionality | Prebuilt function equivalent | +|-------------|-------------------------------------------------------------------------------------------|-----------------------------------| +|&, + |Concatenation operators. Operands will always be cast to string. Examples: A & B, 'foo' + ' bar' => 'foo bar', 'foo' + 3 => 'foo3', 'foo' + (3 + 3) => 'foo6'|N/A| +|' |Used to wrap a string literal. Example: 'myValue' |N/A | +|" |Used to wrap a string literal. Example: "myValue" |N/A | +|\[\] |Used to refer to an item in a list by its index. Example: A[0] |N/A | +|${} |Used to denote an expression. Example: ${A == B}. |N/A | +|${} |Used to denote a variable in template expansion. Example: ${myVariable} |N/A | +|\(\) |Enforces precedence order and groups sub expressions into larger expressions. Example: (A+B)\*C |N/A | +|\. |Property selector. Example: myObject.Property1 |N/A | +|\\ |Escape character for templates, expressions. |N/A | + +--- + +## Variables + +Variables are always referenced by their name in the format `${myVariable}`. They can be referenced either by the property selector operator in the form of `myParent.myVariable`, using the item index selection operator like in `myParent.myList[0]`, or by the [getProperty()](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#getProperty) function. + +There are two special variables. **[]** represents an empty list, and **{}** represents a empty object. + +## Explicit values + +Explicit values can be enclosed in either single quotes 'myExplicitValue' or double quotes "myExplicitValue". + +## Functions + +An adaptive expression has one or more functions. For more information about functions supported by adaptive expressions, see the [prebuilt functions](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md) reference article. + +## Bot Framework Composer + +Bot Framework Composer is an open-source visual authoring canvas for developers and multidisciplinary teams to build bots. Composer uses adaptive expressions to create, calculate, and modify values. Adaptive expressions can be used in language generation template definitions and as properties in the authoring canvas. As seen in the example below, properties in memory can also be used in an adaptive expression. + +The expression `(dialog.orderTotal + dialog.orderTax) > 50` adds the values of the properties `dialog.orderTotal` and `dialog.orderTax`, and evaluates to `True` if the sum is greater than 50 or `False` if the sum is 50 or less. + +Read [Conversation flow and memory](/composer/concept-memory) for more information about how expressions are used in memory. + +## Language generation + +Adaptive expressions are used by language generation (LG) systems to evaluate conditions described in LG templates. In the example below, the [join](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#join) prebuilt function is used to list all values in the `recentTasks` collection. + +```lg +# RecentTasks +- IF: ${count(recentTasks) == 1} + - Your most recent task is ${recentTasks[0]}. You can let me know if you want to add or complete a task. +- ELSEIF: ${count(recentTasks) == 2} + - Your most recent tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task. +- ELSEIF: ${count(recentTasks) > 2} + - Your most recent ${count(recentTasks)} tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task. +- ELSE: + - You don't have any tasks. +``` + +Read the [using prebuilt function in variations](../file-format/bot-builder-lg-file-format.md#using-prebuilt-functions-in-variations) section of the [.lg file format](../file-format/bot-builder-lg-file-format.md) article for more information. + +## Adaptive Cards templating + +[Adaptive Cards templating](/adaptive-cards/templating/) can be used by developers of bots and other technologies to separate data from the layout in an Adaptive Card. Developers can provide [data inline](/adaptive-cards/templating/language#option-a-inline-data) with the `AdaptiveCard` payload, or the more common approach of [separating the data from the template](/adaptive-cards/templating/language#option-b-separating-the-template-from-the-data). + +For example, say you have the following data: + +```json +{ + "id": "1291525457129548", + "status": 4, + "author": "Matt Hidinger", + "message": "{\"type\":\"Deployment\",\"buildId\":\"9542982\",\"releaseId\":\"129\",\"buildNumber\":\"20180504.3\",\"releaseName\":\"Release-104\",\"repoProvider\":\"GitHub\"}", + "start_time": "2018-05-04T18:05:33.3087147Z", + "end_time": "2018-05-04T18:05:33.3087147Z" +} +``` + +The `message` property is a JSON-serialized string. To access the values within the string, the [json](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#json) prebuilt function can be called: + +```json +{ + "type": "TextBlock", + "text": "${json(message).releaseName}" +} +``` + +And will result to the following object: + +```json +{ + "type": "TextBlock", + "text": "Release-104" +} +``` + +For more information and examples see the [adaptive cards templating documentation](/adaptive-cards/templating/). + +## Additional resources + +- [NuGet AdaptiveExpressions](https://www.nuget.org/packages/AdaptiveExpressions) package for C# +- [npm adaptive-expressions](https://www.npmjs.com/package/adaptive-expressions) package for JavaScript +- [Prebuilt functions](../adaptive-expressions/adaptive-expressions-prebuilt-functions.md) supported by the adaptive expressions library +- [C# API reference](/dotnet/api/adaptiveexpressions) +- [JavaScript API reference](/javascript/api/adaptive-expressions) + +[1]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#add +[2]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#sub +[3]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#mul +[4]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#div +[5]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#exp +[6]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#mod +[7]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#equals +[8]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#not +[9]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#greater +[10]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#less +[11]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#greaterOrEquals +[12]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#lessOrEquals +[13]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#and +[14]:../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#or +[15]:https://botbuilder.myget.org/feed/botbuilder-declarative/package/nuget/Microsoft.Bot.Builder.Expressions +[20]:https://github.com/microsoft/BotBuilder-Samples/blob/master/experimental/language-generation/README.md diff --git a/articles/v4sdk/bot-builder-concept-authentication-types.md b/articles/v4sdk/bot-builder-concept-authentication-types.md new file mode 100644 index 000000000..d50d5190d --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-authentication-types.md @@ -0,0 +1,129 @@ +--- +title: Bot Framework authentication types +description: Learn about bot authentication types in the Azure AI Bot Service. +keywords: Azure AI Bot Service, authentication, bot framework token service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Authentication types + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +In the Bot Framework, two broad authentication categories exist: **bot authentication** and **user authentication**. Each has an associated **token** to allow access to secured resources. The following figure shows the elements involved in both bot and user authentication. + +:::image type="content" source="media/concept-bot-authentication/bot-framework-auth-context.png" alt-text="Diagram illustrating the difference between the token for a bot and the token for a user."::: + +In this figure: + +- **Host Platform** is the bot hosting platform. It can be Azure or any host platform you chose. +- **Bot Connector Service** facilitates communication between a bot and a channel. It converts messages received from channels into activity objects, and sends them to the bot's messaging endpoint. Likewise, it converts activity objects received from the bot into messages understood by the channel and sends them to the channel. +- **Bot Adapter** is the default Bot Framework adapter. It: + - Converts the JSON payload into an activity object. + - Creates a turn context and adds the activity object to it. + - Runs middleware, if any. + - Forwards the turn context to the bot. + +> [!NOTE] +> When a custom channel adapter is used, the adapter itself performs the tasks that the Bot Connector Service and the default Bot Adapter do. Also, it provides the authentication mechanism for the related web hook API. For an example, +see [Connect a bot to Slack using the Slack adapter](../bot-service-channel-connect-slack.md?tabs=adapter#connect-a-bot-to-slack-using-the-slack-adapter). + +## Bot authentication + +A bot is identified by its **MicrosoftAppID** and **MicrosoftAppPassword**, which are kept within the bot's settings files (`appsettings.json` (.NET), `.env` (JavaScript), `config.py` (Python)) or in a secrets or key manager. +For more information, see [MicrosoftAppID and MicrosoftAppPassword](../bot-service-manage-overview.md#bot-identity-information). + +When you register a bot in the Azure portal, Azure creates an Microsoft Entra ID registration application. If you use the Bot Framework CLI, you must specifically perform a step to create the Microsoft Entra ID registration. This registration has an application ID (`MicrosoftAppID`) and client secret (`MicrosoftAppPassword`). Azure uses these values to generate a **token** with which the bot can access secure resources. + +When a channel sends a request to a bot, via the Bot Connector service, it specifies a **token** in the **Authorization header** of the request. The bot authenticates calls from the Bot Connector service by verifying the authenticity of the token. + +When the bot sends a request to a channel via the **Bot Connector service**, it must specify the **token** in the **Authorization header** of the request. +All requests must include the access token, which is verified by the Bot Connector service to authorize the request. + +The operations described are automatically performed by the Bot Framework SDK. + +For more information, see the REST API documentation on how to [authenticate requests from the Bot Connector service to your bot](../rest-api/bot-framework-rest-connector-authentication.md#connector-to-bot) and [authenticate requests from your bot to the Bot Connector service](../rest-api/bot-framework-rest-connector-authentication.md#bot-to-connector). + +### Channels + +Typically, channels communicate with a bot via the **Bot Connector service**, so the previous authentication principles generally apply. +Some channels and features have unique authentication considerations. + +#### Direct Line + +Besides the standard supported channels, a client application can communicate with a bot using the Direct Line channel. + +The client application authenticates requests to Direct Line (version 3.0) either by using a **secret** obtained from the [Direct Line channel configuration](../bot-service-channel-connect-directline.md) page in the Azure portal or, better, by using a **token** obtained at runtime. The secret or token is specified in the Authorization header of each request. + +> [!IMPORTANT] +> When you use Azure AI Bot Service authentication with Web Chat there are some important security considerations you must keep in mind. For more information, see the [security considerations](../bot-service-channel-connect-webchat.md#keep-your-secret-hidden-exchange-your-secret-for-a-token-and-generate-the-embed) section in the REST authentication article. + +For more information, see [Keep your secret hidden, exchange your secret for a token, and generate the embed](../bot-service-channel-connect-webchat.md#keep-your-secret-hidden-exchange-your-secret-for-a-token-and-generate-the-embed). + +#### Web Chat + +The Web Chat has two implementations: the **channel** and the **control**. + +- When you register a bot with Azure, the Web Chat channel is automatically configured to allow testing of the bot. + For more information, see [Connect a bot to Web Chat](../bot-service-channel-connect-webchat.md). +- You can use a Web Chat control with the Direct Line channel to provide access to a bot in a client application. For more information about the control, see [Bot Framework Web Chat](https://github.com/microsoft/BotFramework-WebChat). + +### Skills + +A skill and a skill consumer are two distinct bots, each with their own app ID and password. + +- The consumer can forward user activities to a skill and forward the skill's responses to the user. +- To the skill, the skill consumer acts as a channel. The consumer has a skill host endpoint that acts as the service URL that the skill sends activities to. +- For more information about skills, see the [skills overview](skills-conceptual.md). + +Service-level authentication is managed by the Bot Connector service. The framework uses bearer tokens and bot application IDs to verify the identity of each bot. + +> [!IMPORTANT] +> This requires all bots (the skill consumer and any skills it consumes) to have valid application credentials. + +#### Claims validation + +In addition to this basic level of authentication, you must add a _claims validator_ to the authentication configuration of the skill and the skill consumer. The claims are evaluated after the authentication header. This process allows each bot to restrict which other bots it will accept activities from. + +For sample claims validation, see how to [implement a skill](skill-implement-skill.md) and [implement a skill consumer](skill-implement-consumer.md). + +### Bot Framework Emulator + +The Bot Framework Emulator has its own authentication flow and its own tokens. The Emulator has its own channel and a built-in server. + +## User authentication + +At times, a bot must access secured online resources on behalf of the user. To do that the bot must be authorized. This is because to perform certain operations such as checking email, checking on flight status, or placing an order, the bot needs to call an external service such as Microsoft Graph, GitHub, or a company's REST service. OAuth is used to authenticate the user and authorize the bot. + +> [!NOTE] +> Two macro-steps are involved for a bot to access a user's resources. +> +> 1. **Authentication**. The process of verifying the user's identity. +> 1. **Authorization**. The process of verifying that the bot can access the user's resources. +> +> If the first step is successful then a token based on the user's credentials is issued. In the second step, the bot uses the token to access the user's resources. + +For more information, see [User authentication](bot-builder-concept-authentication.md). + +### Identity providers + +An identity provider authenticates user or client identities and issues consumable security tokens. It provides user authentication as a service. Client applications, such as web applications, delegate authentication to a trusted identity provider. + +A bot can use a trusted identity provider to: + +- Enable single sign-on (SSO) features, allowing it to access multiple secured resources. +- Connect to cloud computing resources on behalf of a user, decreasing the need for users to reauthenticate. + +> [!NOTE] +> The token issued during **Bot authentication** isn't the same token issued during **User authentication**. The first is used to establish secure communication between a bot, channels and, ultimately, client applications. The second is used to authorize the bot to access secured resource on behalf of the user. + +Notice that channels provide their own, separate user authentication to let a user sign in to the channel. + +See [Identity providers](bot-builder-concept-identity-providers.md) for more about how bots can use identity providers to access resources on behalf of a user. diff --git a/articles/v4sdk/bot-builder-concept-authentication.md b/articles/v4sdk/bot-builder-concept-authentication.md index 86cba5b35..118e363b4 100644 --- a/articles/v4sdk/bot-builder-concept-authentication.md +++ b/articles/v4sdk/bot-builder-concept-authentication.md @@ -1,34 +1,48 @@ --- -title: User authentication in the Azure Bot Service - Bot Service -description: Learn about user authentication features in the Azure Bot Service. -keywords: azure bot service, authentication, bot framework token service +title: User authentication in the Azure AI Bot Service - Bot Service +description: Learn about user authentication features in the Azure AI Bot Service. See how bots use OAuth connections to sign in users and access secured online resources. +keywords: Azure AI Bot Service, authentication, bot framework token service author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/31/2019 +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: overview +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- -# User authentication within a conversation +# User authentication + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +At times a bot must access secured online resources on behalf of the user, such as checking email, checking on flight status, or placing an order. The user must authorize the bot to do so on their behalf, and in order to authorize the bot, the user must authenticate their identity. _OAuth_ is used to authenticate the user and authorize the bot. See also [Authentication types](bot-builder-concept-authentication-types.md). + +If you want to refresh your OAuth knowledge, see the following: + +- [Good OAuth overview](https://aaronparecki.com/oauth-2-simplified/) easier to follow than the formal specification +- [OAuth specification](https://oauth.net/2/) + +## User authentication in a conversation To perform certain operations on behalf of a user, such as checking email, referencing a calendar, checking on flight status, or placing an order, the bot will need to call an external service, such as the Microsoft Graph, GitHub, or a company's REST service. -Each external services has a way of securing those calls, and a common way to secure such a call is to issue those requests using a _user token_ that uniquely identifies the user on that external service (sometimes referred to as a JWT). +Each external service has a way of securing those calls. A common way to issue those requests is to use a _user token_ that uniquely identifies the user on that external service (sometimes referred to as a [JSON Web Token](https://jwt.io/introduction/) (JWT)). To secure the call to an external service, the bot must ask the user to sign-in, so it can acquire the user's token for that service. -Many services support token retrieval via the OAuth or OAuth2 protocol. -The Azure Bot Service provides specialized sign-in cards and services that work with the OAuth protocol and manage the token life-cycle, and a bot can use these features to acquire a user token. +Many services support token retrieval via the **OAuth** or **OAuth2** protocol. + +The Azure AI Bot Service provides specialized **sign-in** cards and services that work with the OAuth protocol and manage the token life-cycle. A bot can use these features to acquire a user token. -- As part of bot configuration, an _OAuth connection setting_ is registered within the Azure Bot Service resource in Azure. +- As part of bot configuration, an **OAuth connection** is registered within the Azure AI Bot Service resource in Azure. - Each connection setting contains information about the external service or identity provider to be use, along with a valid OAuth client id and secret, the OAuth scopes to enable, and any other connection metadata required by that external service or identity provider. + The connection contains information about the **identity provider** to use, along with a valid OAuth client ID and secret, the OAuth scopes to enable, and any other connection metadata required by that identity provider. -- In the bot's code, an OAuth connection setting is used to help sign in a user and get a user token. +- In the bot's code, the OAuth connection is used to help sign-in the user and get the user token. -These services are involved in the sign-in workflow. +The following image shows the elements involved in the authentication process. -![Authentication overview](./media/bot-builder-concept-authentication.png) +:::image type="content" source="media/concept-bot-authentication/bot-auth-components.png" alt-text="Diagram illustrating the relationship between authentication components in Azure AI Bot Service."::: ## About the Bot Framework Token Service @@ -37,37 +51,59 @@ The Bot Framework Token Service is responsible for: - Facilitating the use of the OAuth protocol with a wide variety of external services. - Securely storing tokens for a particular bot, channel, conversation, and user. - Acquiring user tokens. - > [!TIP] - > If the bot has an expired user token, the bot should: - > - Log the user out - > - Initiate the sign in flow again + > [!TIP] + > If the bot has an expired user token, the bot should: + > + > - Log the user out + > - Initiate the sign-in flow again + +For example, a bot that can check a user's recent emails, using the Microsoft Graph API, requires a user token from an **Identity Provider**, in this case **Microsoft Entra ID**. At design time, the bot developer performs these two important steps: -For example, a bot that can check a user's recent emails, using the Microsoft Graph API, will require an Azure Active Directory user token. At design time, the bot developer would register an Azure Active Directory application with the Bot Framework Token Service (via the Azure Portal), and then configure an OAuth connection setting ( named `GraphConnection`) for the bot. When a user interacts with the bot, the workflow would be: +1. Registers an Microsoft Entra ID application, an Identity Provider, with the Bot Framework Token Service, via the Azure portal. +1. Configures an OAuth connection (named for example `GraphConnection`) for the bot. -1. The user asks the bot, "please check my email". +The following picture shows the time sequence of the user's interaction with a bot when an email request is made using the Microsoft Graph service. + +:::image type="content" source="media/concept-bot-authentication/bot-auth-time-sequence.PNG" alt-text="Sequence diagram outlining the steps for a bot to send an email on behalf of a user."::: + +1. The user makes an email request to the bot. 1. An activity with this message is sent from the user to the Bot Framework channel service. The channel service ensures that the `userid` field within the activity has been set and the message is sent to the bot. - User ID's are channel specific, such as the user's facebook ID or their SMS phone number. + > [!NOTE] + > User ID's are channel specific, such as the user's Facebook ID or their SMS phone number. + +1. The bot makes a request to the Bot Framework Token Service asking if it already has a token for the UserId for the OAuth connection `GraphConnection`. +1. Since this is the first time this user has interacted with the bot, the Bot Framework Token Service doesn't yet have a token for this user, and returns a _NotFound_ result to the bot. -1. The bot receives the message activity and determines that the intent is to check the user's email. The bot makes a request to the Bot Framework Token Service asking if it already has a token for the UserId for the OAuth connection setting `GraphConnection`. -1. Since this is the first time this user has interacted with the bot, the Bot Framework Token Service does not yet have a token for this user, and returns a _NotFound_ result to the bot. -1. The bot creates an OAuthCard with a connection name of `GraphConnection` and replies to the user asking them to sign-in using this card. + > [!NOTE] + > If the token is found, the authentication steps are skipped and the bot can make the email request using the stored token. + +1. The bot creates an OAuthCard with a connection name of `GraphConnection` and replies to the user asking to sign-in using this card. 1. The activity passes through the Bot Framework Channel Service, which calls into the Bot Framework Token Service to create a valid OAuth sign-in URL for this request. This sign-in URL is added to the OAuthCard and the card is returned to the user. 1. The user is presented with a message to sign-in by clicking on the OAuthCard's sign-in button. 1. When the user clicks the sign-in button, the channel service opens a web browser and calls out to the external service to load its sign-in page. -1. The user signs-in to this page for the external service. Once complete, the external service completes the OAuth protocol exchange with the Bot Framework Token Service, resulting in the external service sending the Bot Framework Token Service the user token. The Bot Framework Token Service securely stores this token and sends an activity to the bot with this token. -1. The bot receives the activity with the token and is able to use it to make calls against the Graph API. +1. The user signs-in to this page for the external service. Then the external service completes the OAuth protocol exchange with the Bot Framework Token Service, resulting in the external service sending the Bot Framework Token Service the user token. The Bot Framework Token Service securely stores this token and sends an activity to the bot with this token. +1. The bot receives the activity with the token and is able to use it to make calls against the MS Graph API. ## Securing the sign-in URL -An important consideration when the Bot Framework facilitates a user login is how to secure the sign-in URL. When a user is presented with a sign-in URL, this URL is associated with a specific conversation ID and user ID for that bot. This URL should not be shared, as it would cause the wrong sign-in to occur for a particular bot conversation. To mitigate security attacks regarding sharing the sign-in URL, it is necessary to ensure that the machine and person who clicks on the sign-in URL is the person who _owns_ the conversation window. +An important consideration when the Bot Framework facilitates a user login is how to secure the sign-in URL. When a user is presented with a sign-in URL, this URL is associated with a specific conversation ID and user ID for that bot. Don't share this URL—it would cause the wrong sign-in to occur for a particular bot conversation. To mitigate security attacks that use a shared sign-in URL, ensure that the machine and person who clicks on the sign-in URL is the person who _owns_ the conversation window. + +Some channels such as Microsoft Teams, Direct Line, and WebChat are able to do this without the user noticing. For example, WebChat uses session cookies to ensure that the sign-in flow took place in the same browser as the WebChat conversation. However, for other channels the user is often presented with a 6-digit _magic code_. This is similar to a built-in multi-factor authentication, as the Bot Framework Token Service won't release the token to the bot unless the user finishes the final authentication, proving that the person who signed-in has access to the chat experience by entering the 6-digit code. + +> [!IMPORTANT] +> Please, keep in mind these important [Security considerations](~/rest-api/bot-framework-rest-direct-line-3-0-authentication.md#security-considerations). +> You can find additional information in this blog post: [Using WebChat with Azure AI Bot Service Authentication](https://blog.botframework.com/2018/09/01/using-webchat-with-azure-bot-services-authentication/). -Some channels such as Cortana, Teams, Direct Line, and WebChat are able to do this without the user noticing. For example, WebChat uses session cookies to ensure that the sign-in flow took place in the same browser as the WebChat conversation. However, for other channels the user is often presented with a 6-digit _magic code_. This is similar to a built-in multi-factor authentication, as the Bot Framework Token Service will not release the token to the bot unless the user finishes the final authentication, proving that the person who signed-in has access to the chat experience by entering the 6-digit code. +## Next steps -## Azure Activity Directory application registration +Now that you know about user authentication, let's take a look at how to apply that to your bot. -Every bot that is registered as an Azure Bot Service uses an Azure Activity Directory (AD) application ID. It is important **not** to re-use this application ID and password to sign-in users. The Azure Bot Service's Azure AD application ID is to secure the service to service communication between the bot and the Bot Framework Channel Services. If you want to sign-in users to Azure AD, you should create a separate Azure AD application registration with the appropriate permissions and scopes. +> [!div class="nextstepaction"] +> [Add authentication to a bot](bot-builder-authentication.md) -## Configure an OAuth connection setting +## See also -For more on how to register and use an OAuth connection setting, see [Add authentication to your bot](bot-builder-authentication.md). +- [Identity providers](bot-builder-concept-identity-providers.md) +- [REST Connector authentication](/azure/bot-service/rest-api/bot-framework-rest-connector-authentication?view=azure-bot-service-4.0&preserve-view=true) +- [REST Direct Line authentication](/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0&preserve-view=true) diff --git a/articles/v4sdk/bot-builder-concept-dialog.md b/articles/v4sdk/bot-builder-concept-dialog.md index fada1b67c..43237b536 100644 --- a/articles/v4sdk/bot-builder-concept-dialog.md +++ b/articles/v4sdk/bot-builder-concept-dialog.md @@ -1,212 +1,146 @@ --- -title: Dialogs within the Bot Framework SDK - Bot Service -description: Describes what a dialog is and how it work within the Bot Framework SDK. -keywords: conversation flow, prompt, dialog state, recognize intent, single turn, multiple turn, bot conversation, dialogs, prompts, waterfalls, dialog set -author: johnataylor -ms.author: johtaylo -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +title: Dialogs in the Bot Framework SDK +description: Learn about Bot Framework SDK dialogs. Understand dialog classes and features, different types of dialogs, and dialog design patterns. +keywords: conversation flow, dialogs, dialog state, bot conversation, dialog set, dialog context, dialog stack +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Dialogs library -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -*Dialogs* are a central concept in the SDK, and provide a useful way to manage a conversation with the user. Dialogs are structures in your bot that act like functions in your bot's program; each dialog is designed to perform a specific task, in a specific order. You can specify the order of individual dialogs to guide the conversation, and invoke them in different ways - sometimes in response to a user, sometimes in response to some outside stimuli, or from other dialogs. +_Dialogs_ are a central concept in the SDK, providing ways to manage a long-running conversation with the user. +A dialog performs a task that can represent part of or a complete conversational thread. +It can span just one turn or many, and can span a short or long period of time. -The dialogs library provides a few built-in features, such as *prompts* and *waterfall dialogs* to make your bot's conversation easier to manage. [Prompts](#prompts) are used to ask for different types of information, such as text, a number, or a date. [Waterfall dialogs](#waterfall-dialogs) can combine multiple steps together in a sequence, allowing your bot to follow easily that predefined sequence and pass information along to the next step. +This article describes the core classes and features of the dialog library. - +- You should be familiar with [how bots work](bot-builder-basics.md) (including [what a turn is](bot-builder-basics.md#defining-a-turn)) and [managing state](bot-builder-concept-state.md). +- Each dialog represents a conversational task that can run to completion and return collected information. +- Each dialog represents a basic unit of control flow: it can begin, continue, and end; pause and resume; or be canceled. +- Dialogs are similar to a method or function in a programming language. You can pass in arguments or parameters when you start a dialog, and the dialog can later produce a return value when it ends. -## Dialogs and their pieces - -The dialogs library has a few additional pieces included to make dialogs more useful. Besides the different [types of dialogs](#dialog-types) discussed below, the library contains the idea of a *dialog set*, the *dialog context*, and the *dialog result*. - -*Dialog sets* are, in the simplest terms, a collection of dialogs. This can be things like prompts, waterfall dialogs, or [component dialogs](#component-dialog). Each of these are implementations of a dialog, and each are added to the dialog set with a specific string ID. When your bot wants to start a certain dialog or prompt within the dialog set, it uses that string ID to specify which dialog to use. - -*Dialog context* contains information pertaining to the dialog, and is used to interact with a dialog set from within your bot's turn handler. The dialog context includes the current turn context, the parent dialog, and the [dialog state](#dialog-state), which provides a method for preserving information within the dialog. The dialog context allows you to start a dialog with its string ID or continue the current dialog (such as a waterfall dialog that has multiple steps). - -When a dialog ends, it can return a *dialog result* with some resulting information from the dialog. This is returned to let the calling method see what happened within the dialog and save the information to some persisted location, if desired. +> [!TIP] +> If you're new to developing bots with the Bot Framework or creating a new conversational experience, start with the [Bot Framework Composer](/composer/). +> For existing SDK-first bots, not created in Composer, consider exposing your bot as a [skill](skills-conceptual.md) and using Composer for future bot development. ## Dialog state -Dialogs are an approach to implementing a multi-turn conversation, and as such, they are an example of a feature in the SDK that relies on persisted state across multiple turns. Without state in dialogs, your bot wouldn't know where in the dialog set it is or what information it has already gathered. - -A dialog based bot typically holds a dialog set collection as a member variable in its bot implementation. That dialog set is created with a handle to an object called an accessor that provides access to persisted state. For background on state within bots, see [managing state](bot-builder-concept-state.md). - -Within the bot’s on turn handler, the bot initializes the dialog subsystem by calling *create context* on the dialog set, which returns a *dialog context*. That dialog context contains the necessary information needed by the dialog. - -The creation of a dialog context requires state, which is accessed with the accessor provided when creating the dialog set. With that accessor, the dialog set can get the appropriate dialog state. Details on state accessors can be found in [Save conversation and user data](bot-builder-howto-v4-state.md). - -## Dialog types - -Dialogs come in a few different types: prompts, waterfall dialogs, and component dialogs, as shown in this class hierarchy. - -![dialog classes](media/bot-builder-dialog-classes.png) - -### Prompts - -Prompts, within the dialogs library, provide an easy way to ask the user for information and evaluate their response. For example for a *number prompt*, you specify the question or information you are asking for, and the prompt automatically checks to see if it received a valid number response. If it did, the conversation can continue; if it didn't, it will re-prompt the user for a valid answer. - -Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or starts from the top with a reprompt. - -Prompts have *prompt options* given when the prompt is called, which is where you can specify the text to prompt with, the retry prompt if validation fails, and choices to answer the prompt. In general, the prompt and retry prompt properties are activities, though there is some variation on how this is handled in different programming languages. - -Additionally, you can choose to add some custom validation for your prompt when you create it. For example, say we wanted to get a party size using the number prompt, but that party size has to be more than 2 and less than 12. The prompt first checks to see if it received a valid number, then runs the custom validation if it is provided. If the custom validation fails, it will re-prompt the user as above. - -When a prompt completes, it explicitly returns the resulting value that was asked for. When that value is returned, we can be sure it has passed both the built in prompt validation and any additional custom validation that may have been provided. - -For examples on using various prompts, take a look at how to use the [dialogs library to gather user input](bot-builder-prompts.md). - -#### Prompt types +Dialogs can implement a _multi-turn conversation_, and as such, they rely on _persisted state_ across turns. Without state in dialogs, your bot wouldn't know where it was in the conversation or what information it had already gathered. -Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or restarts from the top with a re-prompt. The dialogs library offers a number of basic prompts, each used for collecting a different type of response. The basic prompts can interpret natural language input, such as "ten" or "a dozen" for a number, or "tomorrow" or "Friday at 10am" for a date-time. +To retain a dialog's place in the conversation, the dialog's state must be retrieved from and saved to memory each turn. This is handled via a dialog state property accessor defined on the bot's conversation state. This dialog state manages information for all active dialogs, and children of active dialogs. +This allows the bot to pick up where it left off last and to handle various conversation models. -| Prompt | Description | Returns | -|:----|:----|:----| -| _Attachment prompt_ | Asks for one or more attachments, such as a document or image. | A collection of _attachment_ objects. | -| _Choice prompt_ | Asks for a choice from a set of options. | A _found choice_ object. | -| _Confirm prompt_ | Asks for a confirmation. | A Boolean value. | -| _Date-time prompt_ | Asks for a date-time. | A collection of _date-time resolution_ objects. | -| _Number prompt_ | Asks for a number. | A numeric value. | -| _Text prompt_ | Asks for general text input. | A string. | +At run time, the dialog state property includes information on where the dialog is in its logical process, including any internally collected information in the form of a _dialog instance_ object. Again, this needs to be read into the bot and saved out to memory each turn. -To prompt a user for input, define a prompt using one of the built-in classes, such as the _text prompt_, and add it to your dialog set. Prompts have fixed IDs that must be unique within a dialog set. You can have a custom validator for each prompt, and for some prompts, you can specify a _default locale_. +## Dialog infrastructure -#### Prompt locale +Along with various types of dialogs, the following classes are involved in the design and control of conversations. +While you don't usually need to interact with these classes directly, being aware of them and their purpose is useful when designing dialogs for a bot. -The locale is used to determine language-specific behavior of the **choice**, **confirm**, **date-time**, and **number** prompts. For any given input from the user, if the channel provided a _locale_ property in user's message, then that is used. Otherwise, if the prompt's _default locale_ is set, by providing it when calling the prompt's constructor or by setting it later, then that is used. If neither of those are provided, English ("en-us") is used as the locale. Note: The locale is a 2, 3, or 4 character ISO 639 code that represents a language or language family. +| Class | Description +| :-- | :-- +| _Dialog set_ | Defines a collection of dialogs that can reference each other and work in concert. +| _Dialog context_ | Contains information about all active dialogs. +| _Dialog instance_ | Contains information about one active dialog. +| _Dialog turn result_ | Contains status information from an active, or recently active, dialog. If the active dialog ended, this contains its return value. -### Waterfall dialogs - -A waterfall dialog is a specific implementation of a dialog that is commonly used to collect information from the user or guide the user through a series of tasks. Each step of the conversation is implemented as an asynchronous function that takes a *waterfall step context* (`step`) parameter. At each step, the bot [prompts the user for input](bot-builder-prompts.md) (or can begin a child dialog, but that it is often a prompt), waits for a response, and then passes the result to the next step. The result of the first function is passed as an argument into the next function, and so on. - -The following diagram shows a sequence of waterfall steps and the stack operations that take place. Details on the use of the dialog stack are below in the [using dialogs](#using-dialogs) section. - -![Dialog concept](media/bot-builder-dialog-concept.png) - -Within waterfall steps, the context of the waterfall dialog is stored in its *waterfall step context*. This is similar to the dialog context as it provides access to the current turn context and state. Use the waterfall step context object to interact with a dialog set from within a waterfall step. - -You can handle a return value from a dialog either within a waterfall step in a dialog or from your bot's on turn handler, although you generally only need to check the status of the dialog turn result from your bot's turn logic. -Within a waterfall step, the dialog provides the return value in the waterfall step context's _result_ property. - -#### Waterfall step context properties - -The waterfall step context contains the following: - -* *Options*: contains input information for the dialog. -* *Values*: contains information you can add to the context, and is carried forward into subsequent steps. -* *Result*: contains the result from the previous step. - -Additionally, the *next* method (**NextAsync** in C#, **next** in JS) continues to the next step of the waterfall dialog within the same turn, enabling your bot to skip a certain step if needed. - -#### Prompt options - -The second parameter of the step context's _prompt_ method takes a _prompt options_ object, which has the following properties. - -| Property | Description | -| :--- | :--- | -| _Prompt_ | The initial activity to send the user, to ask for their input. | -| _Retry prompt_ | The activity to send the user if their first input did not validate. | -| _Choices_ | A list of choices for the user to choose from, for use with a choice prompt. | -| _Validations_ | Additional parameters to use with a custom validator. | -| _Style_ | Defines how the choices for a choice prompt or confirm prompt will be presented to a user. | - -You should always specify the initial prompt activity to send the user, as well as a retry prompt for instances when the user's input doesn't validate. - -If the user's input isn't valid, the retry prompt is sent to the user; if there was no retry specified, then the initial prompt is re-sent. However, if an activity is sent back to the user from within the validator, no retry prompt is sent. - -##### Prompt validation - -You can validate a prompt response before returning the value to the next step of the waterfall. A validator function has a _prompt validator context_ parameter and returns a Boolean, indicating whether the input passes validation. -The prompt validator context includes the following properties: - -| Property | Description | -| :--- | :--- | -| _Context_ | The current turn context for the bot. | -| _Recognized_ | A _prompt recognizer result_ that contains information about the user input, as processed by the recognizer. | -| _Options_ | Contains the _prompt options_ that were provided in the call to start the prompt. | - -The prompt recognizer result has the following properties: +## Dialog types -| Property | Description | -| :--- | :--- | -| _Succeeded_ | Indicates whether the recognizer was able to parse the input. | -| _Value_ | The return value from the recognizer. If necessary, the validation code can modify this value. | +The dialogs library provides a few types of dialogs to make your bot's conversations easier to manage. Some of these types are described in more detail later in this article. -### Component dialog +| Type | Description | +|:-|:-| +| dialog | The base class for all dialogs. | +| [container dialog](#container-dialogs) | The base class for all _container_ dialogs, such as component and adaptive dialogs. It maintains an inner dialog set and allows you to treat a collection of dialogs as a unit. | +| [component dialog](#component-dialogs) | A general-purpose type of container dialog that encapsulates a set of dialogs, allowing for the reuse of the set as a whole. When a component dialog starts, it begins with a designated dialog in its collection. When the inner process completes, the component dialog ends. | +| waterfall dialog | Defines a sequence of steps, allowing your bot to guide a user through a linear process. These are typically designed to work within the context of a component dialog. | +| prompt dialogs | Ask the user for input and return the result. A prompt will repeat until it gets valid input or it's canceled. They're designed to work with waterfall dialogs. | +| adaptive dialog | A type of container dialog used by Composer to provide more natural conversational flows. _Not_ intended to be used directly in an SDK-first bot. | +| action dialogs | A type of dialog that supports the implementation of actions in Composer. _Not_ intended to be used directly in an SDK-first bot. | +| input dialogs | A type of dialog that supports the implementation of input actions in Composer. _Not_ intended to be used directly in an SDK-first bot. | +| [skill dialog](#skill-dialog) | Automates the management of one or more skill bots from a skill consumer. Composer directly supports skills as actions. | +| [QnA Maker dialog](#qna-maker-dialog) | Automates access to a QnA Maker knowledge base. This dialog is designed to also work as an action within Composer. | -Sometimes you want to write a reusable dialog that you want to use in different scenarios, such as an address dialog that asks the user to provide values for street, city and zip code. +> [!IMPORTANT] +> _Adaptive dialogs_ were first added in version 4.9 of the C# SDK. +> Adaptive dialogs support the [Bot Framework Composer](/composer/) and are not intended to be used directly in an SDK-first bot. -The *component dialog* provides a strategy for creating independent dialogs to handle specific scenarios, breaking a large dialog set into more manageable pieces. Each of these pieces has its own dialog set, and avoids any name collisions with the dialog set that contains it. See the [component dialog how to](bot-builder-compositcontrol.md) for more on these. +## Dialog patterns -## Using dialogs +There are two main patterns for starting and managing dialogs from a bot. -You can use the dialog context to begin, continue, replace, or end a dialog. You can also cancel all dialogs on the dialog stack. +1. We recommend using Bot Framework Composer to author conversational dialogs, to benefit from more natural, free-flowing conversational capabilities. For more information, see the [Introduction to Bot Framework Composer](/composer/introduction). Such bots can still be extended with code where needed. +1. Develop your bot in one of the SDK languages and use your root dialog's _run_ extension method. For information on using the run method with a component dialog, see [about component and waterfall dialogs](bot-builder-concept-waterfall-dialogs.md) and how to [implement sequential conversation flow](bot-builder-dialog-manage-conversation-flow.md). -Dialogs can be thought of as a programmatic stack, which we call the *dialog stack*, with the turn handler as the one directing it and serving as the fallback if the stack is empty. The topmost item on that stack is considered the *active dialog*, and the dialog context directs all input to the active dialog. +### The dialog stack -When a dialog begins, it is pushed onto the stack, and is now the active dialog. It remains the active dialog until it either ends, it is removed by the [replace dialog](#repeating-a-dialog) method, or another dialog is pushed onto the stack (by either the turn handler or active dialog itself) and becomes the active dialog. When that new dialog ends, it is popped off the stack and the next dialog down becomes the active dialog again. This allows for [repeating a dialog](#repeating-a-dialog) or [branching a conversation](#branch-a-conversation), discussed below. +A dialog context contains information about all active dialogs and includes a _dialog stack_, which acts as a _call stack_ for all the active dialogs. +Each container dialog has an inner set of dialogs that it's controlling, and so each active container dialog introduces an inner dialog context and dialog stack as part of its state. +While you won't access the stack directly, understanding that it exists and its function will help you understand how various aspects of the dialogs library work. -### Create the dialog context +## Container dialogs -To create your dialog context, call the *create context* method of your dialog set. Create context gets the dialog set's *dialog state* property and uses that to create the dialog context. The dialog context is then used to start, continue, or otherwise control the dialogs in the set. +A container dialog can be part of a larger dialog set. Each container has an inner dialog set that is also managed. -The dialog set requires use of a *state property accessor* to access the dialog state. The accessor is created and used the same way as other state accessors, but is created as it's own property based off of the conversation state. Details on managing state can be found in the [managing state topic](bot-builder-concept-state.md), and usage of dialog state is shown in the [sequential conversation flow](bot-builder-dialog-manage-conversation-flow.md) how-to. +- Each dialog set creates a scope for resolving dialog IDs. +- The SDK currently implements two types of container dialogs: component dialogs and adaptive dialogs. -### To start a dialog + The conceptual structure of the two are quite different. However, a Composer bot can make use of both. -To start a dialog, pass the *dialog ID* you want to start into the dialog context's *begin dialog*, *prompt*, or *replace dialog* method. +### Dialog IDs -* The begin dialog method will push the dialog onto the top of the stack. -* The replace dialog method will pop the current dialog off the stack and push the replacing dialog onto the stack. The replaced dialog is canceled and any information that instance contained is disposed of. +When you add a dialog to a dialog set, you assign it an unique ID within that set. Dialogs within a set reference each other by their IDs. -Use the _options_ parameter to pass information to the new instance of the dialog. -The options passed into the new dialog can be accessed via the step context's *options* property in any step of the dialog. -See the [Create advanced conversation flow using branches and loops](bot-builder-dialog-manage-complex-conversation-flow.md) how-to for example code. +When one dialog references another dialog at run time, it does so by the dialog's ID. The dialog context tries to resolve the ID based on the other dialogs in the immediate dialog set. If there is no match, it looks for a match in the containing or outer dialog set, and so on. If no match is found, an exception or error is generated. -### To continue a dialog +### Component dialogs -To continue a dialog, call the *continue dialog* method. The continue method will always continue the topmost dialog on the stack (the active dialog), if there is one. If the continued dialog ends, control is passed to the parent context which continues within the same turn. +Component dialogs use a sequence model for conversations, and each dialog in the container is responsible for calling other dialogs in the container. When the component dialog's inner dialog stack is empty, the component ends. -Use the step context's *values* property to persist state between turns. -Any value added to this collection in a previous turn is available in subsequent turns. -See the [Create advanced conversation flow using branches and loops](bot-builder-dialog-manage-complex-conversation-flow.md) how-to for example code. +Consider using component and waterfall dialogs if your bot has a relatively simple control flow that doesn't require more dynamic conversation flow. -### To end a dialog +[About component and waterfall dialogs](bot-builder-concept-waterfall-dialogs.md) describes component, waterfall, and prompt dialogs in more detail. -The *end dialog* method ends a dialog by popping it off the stack and returns an optional result to the parent context (such as the dialog that called it, or the bot's turn handler). This is most often called from within the dialog to end the current instance of itself. +## Other dialogs -You can call the end dialog method from anywhere you have a dialog context, but it will appear to the bot that it was called from the current active dialog. +The QnA Maker and skill dialogs can be used as stand-alone dialogs or as part of a collection of dialogs in a container. -> [!TIP] -> It is best practice to explicitly call the *end dialog* method at the end of the dialog. +### QnA Maker dialog -### To clear all dialogs +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] -If you want to pop all dialogs off the stack, you can clear the dialog stack by calling the dialog context's *cancel all dialogs* method. +The QnA Maker dialog accesses a QnA Maker knowledge base and supports QnA Maker's follow-up prompt and active learning features. -### Repeating a dialog +- Follow-up prompts, also known as multi-turn prompts, allow a knowledge base to ask the user for more information before answering their question. +- Active learning suggestions allow the knowledge base to improve over time. The QnA Maker dialog supports explicit feedback for the active learning feature. -You can replace a dialog with itself, creating a loop, by using the *replace dialog* method. -This is a great way to handle [complex interactions](~/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md) and a good technique to manage menus. +For more information, see: -> [!NOTE] -> If you need to persist the internal state for the current dialog, you will need to pass information to the new instance of the dialog in the call to the *replace dialog* method, and then initialize the dialog appropriately. +- [What is QnA Maker?](/azure/ai-services/qnamaker/overview/overview). +- In the SDK, how to [use QnA Maker to answer questions](bot-builder-howto-qna.md). +- In Composer, how to [Add a QnA Maker knowledge base to your bot](/composer/how-to-add-qna-to-bot). -### Branch a conversation +### Skill dialog -The dialog context maintains the dialog stack and for each dialog on the stack, tracks which step is next. Its *begin dialog* method creates a child and pushes that dialog onto the top of the stack, and its *end dialog* method pops the top dialog off the stack. *End dialog* is usually called from within the dialog that's ending. +A skill dialog accesses and manages one or more skills. +The skill dialog posts activities from the parent bot to the skill bot and returns the skill responses to the user. -A dialog can start a new dialog within the same dialog set by calling the dialog context's *begin dialog* method and providing the ID of the new dialog, which then makes the new dialog the currently active dialog. The original dialog is still on the stack, but calls to the dialog context's *continue dialog* method are only sent to the dialog that is on top of the stack, the *active dialog*. When a dialog is popped off the stack, the dialog context will resume with the next step of the waterfall on the stack where it left off of the original dialog. +For more information, see: -Therefore, you can create a branch within your conversation flow by including a step in one dialog that can conditionally choose a dialog to start out of a set of available dialogs. +- In the SDK, the [Skills overview](skills-conceptual.md). +- In Composer, [About skills](/composer/concept-skills). ## Next steps > [!div class="nextstepaction"] -> [Use dialog library to gather user input](bot-builder-prompts.md) +> [About middleware](bot-builder-concept-middleware.md) diff --git a/articles/v4sdk/bot-builder-concept-identity-providers-proxy.md b/articles/v4sdk/bot-builder-concept-identity-providers-proxy.md new file mode 100644 index 000000000..73cbd4566 --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-identity-providers-proxy.md @@ -0,0 +1,234 @@ +--- +title: Identity providers proxy +description: Creating an OAuth2 proxy service to call custom or advanced identity providers in the Azure AI Bot Service. +keywords: Azure AI Bot Service, authentication, identity providers proxy, bot framework token service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Identity providers proxy + +This document explains how to create a proxy to interact with custom or advanced identity providers that use OAuth2 protocol. + +The Bot Framework allows users to log in using various identity providers that use the OAuth2 protocol. However, identity providers can deviate from the core OAuth2 protocol, by offering more advanced capabilities, or alternative sign-in options. In these cases, you may not find an appropriate *connection setting configuration* that works for you. A possible solution is to do the following: + +1. Write an **OAuth2 provider proxy** that is in between the Bot Framework token service and the *more customized or advanced* identity provider. +1. Configure the connection setting to call this proxy, and have this proxy make the calls to the custom or advanced identity provider. The proxy can also map or transform responses to make them conform to what the Bot Framework token service expects. + +## OAuth2 Proxy Service + +To build an **OAuth2 Proxy Service**, you need to implement a REST service with two OAuth2 APIs: one for authorization and one for retrieving a token. Below, you'll find a C# example of each of these methods and what you can do in these methods to call a custom or advanced identity provider. + +### Authorize API + +The authorize API is an **HTTP GET** that authorizes the caller, generates a code property, and redirects to the redirect URI. + +```csharp +[HttpGet("authorize")] +public ActionResult Authorize( + string response_type, + string client_id, + string state, + string redirect_uri, + string scope = null) +{ + // validate parameters + if (string.IsNullOrEmpty(state)) + { + return BadRequest("Authorize request missing parameter 'state'"); + } + + if (string.IsNullOrEmpty(redirect_uri)) + { + return BadRequest("Authorize request missing parameter 'redirect_uri'"); + } + + // redirect to an external identity provider, + // or for this sample, generate a code and token pair and redirect to the redirect_uri + + var code = Guid.NewGuid().ToString("n"); + var token = Guid.NewGuid().ToString("n"); + _tokens.AddOrUpdate(code, token, (c, t) => token); + + return Redirect($"{redirect_uri}?code={code}&state={state}"); +} +``` + +### Token API + +The Token API is an **HTTP POST** that is called by the Bot Framework token service. The Bot Framework token service will send the `client_id` and `client_secret` in the request's body. These values should be validated and/or passed along to the custom or advanced identity provider. +The response to this call is a JSON object containing the `access_token` and expiration value of the token (all other values are ignored). If your identity provider returns an `id_token` or some other value that you want to return instead, you just need to map it to the `access_token` property of your response before you return. + +```csharp +[HttpPost("token")] +public async Task Token() +{ + string body; + + using (var reader = new StreamReader(Request.Body)) + { + body = await reader.ReadToEndAsync(); + } + + if (string.IsNullOrEmpty(body)) + { + return BadRequest("Token request missing body"); + } + + var parameters = HttpUtility.ParseQueryString(body); + string authorizationCode = parameters["code"]; + string grantType = parameters["grant_type"]; + string clientId = parameters["client_id"]; + string clientSecret = parameters["client_secret"]; + string redirectUri= parameters["redirect_uri"]; + + // Validate any of these parameters here, or call out to an external identity provider with them + + if (_tokens.TryRemove(authorizationCode, out string token)) + { + return Ok(new TokenResponse() + { + AccessToken = token, + ExpiresIn = 3600, + TokenType = "custom", + }); + } + else + { + return BadRequest("Token request body did not contain parameter 'code'"); + } +} +``` + +## Proxy Connection Setting Configuration + +Once you have your **OAuth2 Proxy Service** running, you can create an *OAuth Service Provider Connection Setting* on your Azure AI Bot Service resource. Follow the steps described below. + +1. Give a name to the connection setting. +1. Select the **Generic Oauth 2** service provider. +1. Enter a **Client id** and **Client secret** for the connection. These values might be provided by your advanced or custom identity provider, or these could be specific just to your proxy if the identity provider you're using doesn't use client id and secret. +1. For the **Authorization URL**, you should copy the address of your authorization REST API, for example `https://proxy.com/api/oauth/authorize`. +1. For the **Token and Refresh URL**, you should copy the address of your token REST API, for example `https://proxy.com/api/oauth/token`. The Token Exchange URL is valid only for AAD based providers and so can be ignored. +1. Finally, add any scopes that are appropriate. + +## OAuthController for ASP.NET web app + +```csharp +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Threading.Tasks; +using System.Web; + +namespace CustomOAuthProvider.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class OAuthController : ControllerBase + { + ConcurrentDictionary _tokens; + + public OAuthController(ConcurrentDictionary tokens) + { + _tokens = tokens; + } + + [HttpGet("authorize")] + public ActionResult Authorize( + string response_type, + string client_id, + string state, + string redirect_uri, + string scope = null) + { + if (string.IsNullOrEmpty(state)) + { + return BadRequest("Authorize request missing parameter 'state'"); + } + + if (string.IsNullOrEmpty(redirect_uri)) + { + return BadRequest("Authorize request missing parameter 'redirect_uri'"); + } + + // reidrect to an external identity provider, + // or for this sample, generte a code and token pair and redirect to the redirect_uri + + var code = Guid.NewGuid().ToString("n"); + var token = Guid.NewGuid().ToString("n"); + _tokens.AddOrUpdate(code, token, (c, t) => token); + + return Redirect($"{redirect_uri}?code={code}&state={state}"); + } + + [HttpPost("token")] + public async Task Token() + { + string body; + + using (var reader = new StreamReader(Request.Body)) + { + body = await reader.ReadToEndAsync(); + } + + if (string.IsNullOrEmpty(body)) + { + return BadRequest("Token request missing body"); + } + + var parameters = HttpUtility.ParseQueryString(body); + string authorizationCode = parameters["code"]; + string grantType = parameters["grant_type"]; + string clientId = parameters["client_id"]; + string clientSecret = parameters["client_secret"]; + string redirectUri= parameters["redirect_uri"]; + + // Validate any of these parameters here, or call out to an external identity provider with them + + if (_tokens.TryRemove(authorizationCode, out string token)) + { + return Ok(new TokenResponse() + { + AccessToken = token, + ExpiresIn = 3600, + TokenType = "custom", + }); + } + else + { + return BadRequest("Token request body did not contain parameter 'code'"); + } + } + } + + public class TokenResponse + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + + [JsonProperty("id_token")] + public string IdToken { get; set; } + + [JsonProperty("token_type")] + public string TokenType { get; set; } + + [JsonProperty("expires_in")] + public int ExpiresIn { get; set; } + + [JsonProperty("refresh_token")] + public string RefreshToken { get; set; } + + [JsonProperty("scope")] + public string Scope { get; set; } + } +} +``` diff --git a/articles/v4sdk/bot-builder-concept-identity-providers.md b/articles/v4sdk/bot-builder-concept-identity-providers.md new file mode 100644 index 000000000..029efc7ed --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-identity-providers.md @@ -0,0 +1,170 @@ +--- +title: Identity providers in Bot Framework SDK +description: Learn about identity providers, which authenticate user or client identities and issue security tokens. They provide authentication as a service. +keywords: Azure AI Bot Service, authentication, bot framework token service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Identity providers + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +An identity provider authenticates user or client identities and issues consumable security tokens. It provides user authentication as a service. + +Client applications, such as web applications, delegate authentication to a trusted identity provider. Such client applications are said to be federated, that is, they use federated identity. For more information, see [Federated Identity pattern](/azure/architecture/patterns/federated-identity). + +Using a trusted identity provider: + +- Enables single sign-on (SSO) features, allowing an application to access multiple secured resources. +- Facilitates connections between cloud computing resources and users, decreasing the need for users to reauthenticate. + +## Single sign-on + +Single sign-on refers to an authentication process that lets a user log on to a system once with a single set of credentials to access multiple applications or services. + +A user logs in with a single ID and password to gain access to any of several related software systems. For more information, see [Single sign on](./bot-builder-concept-sso.md). + +Many identity providers support a sign-out operation that revokes the user token and terminates access to the associated applications and services. + +> [!IMPORTANT] +> SSO enhances usability by reducing the number of times a user must enter credentials. It also provides better security by decreasing the potential attack surface. + +## Microsoft Entra ID identity provider + +Microsoft Entra ID is the identity service in Microsoft Azure that provides identity management and access control capabilities. It allows you to securely sign in users using industry standard protocols like **OAuth2.0**. + +You can choose from two Active Directory identity provider implementations, which have different settings as shown below. + +> [!NOTE] +> Use these settings when configuring **OAuth Connection Settings** in the Azure bot registration application. For more information, see [Add authentication to a bot](bot-builder-authentication.md). + +# [Microsoft Entra ID](#tab/adv2) + +The Microsoft identity platform (v2.0)—also known as the Microsoft Entra ID endpoint—allows a bot to get tokens to call Microsoft APIs, such as Microsoft Graph or other APIs. The identity platformis an evolution of the Azure AD platform (v1.0). +For more information, see the [Microsoft identity platform (v2.0) overview](/azure/active-directory/develop/active-directory-appmodel-v2-overview). + +Use the AD v2 settings below to enable a bot to access Office 365 data via the Microsoft Graph API. + +| Property | Description or value | +|--|--| +| **Name** | A name for this identity provider connection. | +| **Service Provider** | The identity provider to use. Select **Microsoft Entra ID**. | +| **Client id** | The application (client) ID for your Azure identity provider app. | +| **Client secret** | The secret for your Azure identity provider app. | +| **Tenant ID** | Your directory (tenant) ID or `common`. For more information, see the note about [tenant IDs](#azure-ad-note). | +| **Scopes** | A space-separated list of the API permissions you granted the Microsoft Entra ID identity provider app, such as `openid`, `profile`, `Mail.Read`, `Mail.Send`, `User.Read`, and `User.ReadBasic.All`. | +| **Token Exchange URL** | For an _SSO-enabled skill bot_ use the token exchange URL associated with the OAuth connection; otherwise, leave this empty. For information about the SSO token exchange URL, see [Create an OAuth connection settings](bot-builder-authentication-sso.md#create-an-oauth-connection-setting-1). | + +# [Azure AD v1](#tab/adv1) + +### Azure AD v1 + +Use the settings shown below to configure the Microsoft Entra ID developer platform (v1.0), also known as **Azure AD v1** endpoint. This allows you to build apps that securely sign in users with a Microsoft work or school account. +For more information, see [Microsoft Entra ID for developers (v1.0) overview](/azure/active-directory/azuread-dev/v1-overview). + +| Property | Description or value | +|--|--| +| **Name** | A name for this identity provider connection. | +| **Service Provider** | The identity provider to use. Select **Microsoft Entra ID**. | +| **Client id** | The application (client) ID for your Azure identity provider app. | +| **Client secret** | The secret for your Azure identity provider app. | +| **Grant Type** | `authorization_code` | +| **Login URL** | `https://login.microsoftonline.com` | +| **Tenant ID** | Your directory (tenant) ID or `common`. For more information, see the note below about [tenant IDs](#azure-ad-note). | +| **Resource URL** | `https://graph.microsoft.com/` | +| **Scopes** | Leave this empty. | +| **Token Exchange URL** | Leave this empty. | + +--- + + + +> [!NOTE] +> If you selected one of the following, enter the **tenant ID** you recorded for the Microsoft Entra ID identity provider app: +> +> - **Accounts in this organizational directory only (Microsoft only - Single tenant)** +> - **Accounts in any organizational directory(Microsoft AAD directory - Multi tenant)** +> +> If you selected **Accounts in any organizational directory (Any Microsoft Entra ID directory - Multi tenant and personal Microsoft accounts e.g., Skype, Xbox, Outlook.com)**, enter `common`. +> +> Otherwise, the Microsoft Entra ID identity provider app will use the tenant to verify the selected ID and exclude personal Microsoft accounts. + +For more information, see: + +- [Why update to Microsoft identity platform (v2.0)?](/azure/active-directory/develop/active-directory-v2-compare) +- [Microsoft identity platform (Microsoft Entra ID for developers)](/azure/active-directory/develop/). + +## Other identity providers + +Azure supports several identity providers. You can get a complete list, along with the related details, by running the following Azure console commands: + +```azurecli +az login +az bot authsetting list-providers +``` + +You can also see the list of these providers in the [Azure portal](https://ms.portal.azure.com/) when you define the OAuth connection settings for a bot registration app. + +:::image type="content" source="media/concept-bot-authentication/bot-auth-identity-providers.png" alt-text="Azure identity providers"::: + +### OAuth generic providers + +Azure supports generic OAuth2, which allows you to use your own identity provider. + +You can choose from two generic identity provider implementations, which have different settings as shown below. + +> [!NOTE] +> Use the settings described here when configuring the **OAuth Connection Settings** in the Azure bot registration application. + +### [Generic OAuth 2](#tab/ga2) + +Use this provider to configure any generic OAuth2 identity provider that has similar expectations as Microsoft Entra ID provider, particularly AD v2. For this connection type, the query strings and request body payloads are fixed. + +| Property | Description or value | +|--|--| +| **Name** | A name for this identity provider connection. | +| **Service Provider** | The identity provider to use. Select **Generic Oauth 2**. | +| **Client id** | Your client ID obtained from the identity provider. | +| **Client secret** | Your client secret obtained from the identity provider registration. | +| **Authorization URL** | `https://login.microsoftonline.com/common/oauth2/v2.0/authorize` | +| **Token URL** | `https://login.microsoftonline.com/common/oauth2/v2.0/token` | +| **Refresh URL** | `https://login.microsoftonline.com/common/oauth2/v2.0/token` | +| **Token Exchange URL** | Leave this empty. | +| **Scopes** | A comma-separated list of the API permissions you granted to the identity provider app. | + +### [OAuth 2 Generic Provider](#tab/a2gp) + +This provider requires more configuration parameters, so use this version to configure any generic OAuth 2 service provider when you need more flexibility. With this configuration, you specify the URL templates, the query string templates, and the body templates for authorization, refresh, and token conversion. + +| Property | Description or value | +|--|--| +| **Name** | A name for this identity provider connection. | +| **Service Provider** | The identity provider to use. Select **Oauth 2 Generic Provider**. | +| **Client id** | Your client ID obtained from the identity provider. | +| **Client secret** | Your client secret obtained from the identity provider registration. | +| **Scope List Delimiter** | The separator character for the scope list. Empty spaces ( ) are not supported in this field, but can be used in the **Scopes** field if required by the identity provider. In that case, use a comma (,) for this field, and spaces ( ) in the **Scopes** field. | +| **Authorization URL Template** | A URL template for authorization, defined by your identity provider. For example, `https://login.microsoftonline.com/common/oauth2/v2.0/authorize`. | +| **Authorization URL Query String Template** | A query template for authorization, provided by your identity provider. Keys in the query string template will vary depending on the identity provider. For example, `?client_id={ClientId}&response_type=code&redirect_uri={RedirectUrl}&scope={Scopes}&state={State}`. | +| **Token URL Template** | `https://login.microsoftonline.com/common/oauth2/v2.0/token` | +| **Token URL Query String Template** | The query string separator for the token URL. Usually a question mark (?). | +| **Token Body Template** | A template for the token body. For example, `code={Code}&grant_type=authorization_code&redirect_uri={RedirectUrl}&client_id={ClientId}&client_secret={ClientSecret}`. | +| **Refresh URL Template** | `https://login.microsoftonline.com/common/oauth2/v2.0/token` | +| **Refresh URL Query String Template** | A refresh URL query string separator for the token URL. Usually a question mark (?). | +| **Refresh Body Template** | A template for the refresh body. For example, `refresh_token={RefreshToken}&redirect_uri={RedirectUrl}&grant_type=refresh_token&client_id={ClientId}&client_secret={ClientSecret}`. | +| **Token Exchange URL** |Leave this empty. | +| **Scopes** | List of scopes you want authenticated users to have once signed in. Make sure you're only setting the necessary scopes, and follow the [Least privilege access control principle](/windows-server/identity/ad-ds/plan/security-best-practices/implementing-least-privilege-administrative-models). For example, `User.Read`. If you're using a custom scope, use the full URI including the exposed application ID URI. | + +--- + +## Next steps + +> [!div class="nextstepaction"] +> [Identity providers proxy](bot-builder-concept-identity-providers-proxy.md) diff --git a/articles/v4sdk/bot-builder-concept-language-generation.md b/articles/v4sdk/bot-builder-concept-language-generation.md new file mode 100644 index 000000000..c3de94bef --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-language-generation.md @@ -0,0 +1,60 @@ +--- +title: Language Generation +description: Describes how Language Generation works within the Bot Framework SDK. +keywords: language generation +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Language Generation + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Language Generation (LG) allows developers to extract embedded strings from their code and resource files and manage them through a LG runtime and file format. With LG, developers can create a more natural conversation experience by defining multiple variations on a phrase, executing simple expressions based on context, and referring to conversational memory. + +> [!NOTE] +> LG is supported in [Bot Framework Composer](/composer/introduction) and isn't intended for use in SDK-first bots. + +LG can be used by developers to: + +- achieve a coherent personality, tone of voice for their bot +- separate business logic from presentation +- include variations and sophisticated composition based resolution for any of their bot's replies +- add speech and display adaptations +- construct cards, suggested actions and attachments + +At the core of LG lies template expansion and entity substitution. You can provide one-of variation for expansion as well as conditionally expand a template. The output from LG can be a simple text string, multi-line response, or a complex object payload that a layer above LG will use to construct an [activity][]. + +The following is a simple greeting LG template. Notice that all of the greetings reference the user's name in memory with the variable `${user.name}`. + +```lg +# greetingTemplate +- Hello ${user.name}, how are you? +- Good morning ${user.name}.It's nice to see you again. +- Good day ${user.name}. What can I do for you today? +``` + +## LG in action + +You can use LG in various ways when developing bots. To start, create one or more [.lg file(s)][lg-file-format] to cover all possible scenarios where you would use the language generation sub-system with your bot's replies to a user. + +## Multilingual generation and language fallback policy + +Your bot might target more than one spoken or display languages. You can manage separate instances of the *TemplateEngine*, one per target language. + +## Additional resources + +- See [.lg file format][lg-file-format] for more information about .lg files. +- Read [structured response templates](../language-generation/language-generation-structured-response-template.md) to learn more about complex templates. +- [C# API Reference](/dotnet/api/microsoft.bot.builder.languagegeneration) +- [JavaScript API reference](/javascript/api/botbuilder-lg) + +[activity]: https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md +[lg-file-format]: ../file-format/bot-builder-lg-file-format.md diff --git a/articles/v4sdk/bot-builder-concept-luis.md b/articles/v4sdk/bot-builder-concept-luis.md index f4e01d262..8e696424c 100644 --- a/articles/v4sdk/bot-builder-concept-luis.md +++ b/articles/v4sdk/bot-builder-concept-luis.md @@ -1,101 +1,227 @@ --- -title: Language Understanding - Bot Service -description: Learn how to add artificial intelligence to your bots with Microsoft Cognitive Services to make them more useful and engaging. -keywords: LUIS, intent, recognizer, dispatch tool, qna, qna maker -author: ivorb -ms.author: kamrani -manager: rstand +title: Language understanding +description: Learn how to add artificial intelligence to your bots with Azure AI services to make them more useful and engaging. +keywords: Azure AI services, CLU, LUIS, QnA Maker, custom question answering +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow ms.topic: article -ms.service: bot-service -ms.date: 09/19/2018 -ms.reviewer: +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- +# Natural language understanding -# Language Understanding +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] +Bots can use various conversational styles, from structured and guided to free-form and open-ended. +Based on what a user says, your bot needs to decide what to do next in its conversation flow. +Azure AI services includes features to help with this task. +These features can help a bot search for information, ask questions, or interpret the user's intent. -Bots can use a variety of conversational styles, from the structured and guided to the free-form and open-ended. A bot needs to decide what to do next in its conversation flow, based on what the user said, and in an open-ended conversation, there is a wider range of user replies. +The interaction between users and bots is often free-form, and bots need to understand language naturally and contextually. +In an open-ended conversation, there can be a wide range of user replies, and bots can provide more or less structure or guidance. +This table illustrates the difference between guided and open-ended questions. -| Guided | Open | -|------|------| +| Guided | Open-ended | +|:-----------------------------------------------------------------------------------------------------|:-------------------------------------------------------| | I'm the travel bot. Select one of the following options: find flights, find hotels, find rental car. | I can help you book travel. What would you like to do? | -| Do you need anything else? Click yes or no. | Do you need anything else? | +| Do you need anything else? Click yes or no. | Do you need anything else? | -The interaction between users and bots is often free-form, and bots need to understand language naturally and contextually. This article explains how Language Understanding (LUIS) help you to determine what users want, to identify concepts and entities in sentences, and ultimately to allow your bots to respond with the appropriate action. +Azure AI services provides features with which to build intelligent apps, websites, and bots. +Adding these features to your bot can allow your bot to respond to open-ended user input more appropriately. -## Recognize intent +This article describes support in the Bot Framework SDK for some of the features available in Azure AI services. -[LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/home) helps you by determining the user’s **intent**, which is what they want to do, from what they say, so your bot can respond appropriately. LUIS is especially helpful when what they say to your bot doesn’t follow a predictable structure or a specific pattern. If a bot has a conversational user interface, in which the user speaks or types a response, there can be endless variations on *utterances*, which are the spoken or textual input from the user. +- For tips on how to design these features into your bot, see [Design knowledge bots](../bot-service-design-pattern-knowledge-base.md). +- For detailed information about Azure AI services, see the [Azure AI services documentation](/azure/ai-services/). -For example, consider the many ways a user of a travel bot can ask to book a flight. +## General guidance -![Various differently formed utterances for book a flight](media/cognitive-services-add-bot-language/cognitive-services-luis-utterances.png) +Azure AI services incorporates evolving technologies. +Azure AI Language integrates various features that were previously implemented as separate services. +This article describes both the newer and older features and services, and where to find more information about each. -These utterances can have different structures and contain various synonyms for “flight” that you haven't thought of. In your bot, it can be challenging to write the logic that matches all the utterances, and still distinguishes them from other intents that contain the same words. Additionally, your bot needs to extract *entities*, which are other important words like locations and times. LUIS makes this process easy by contextually identifying intents and entities for you. +| Scenario | Guidance | +|:-|:-| +| New bot development | Consider using Microsoft Copilot Studio, which is designed to support teams where members have a mix of skills and disciplines. For more information, see [Copilot Studio](/microsoft-copilot-studio/fundamentals-what-is-copilot-studio) and [Enable advanced AI features](/microsoft-copilot-studio/advanced-ai-features). | +| New language projects for existing Bot Framework SDK bots | Consider using features of the Azure AI Language service, such as conversational language understanding (CLU) and answering questions. | +| Existing bots with existing language projects | Your language projects will continue to work, but consider migrating to Azure AI Language. For more information, see the [Migrate existing language projects](#migrate-existing-language-projects) section later in this article. | -When you design your bot for natural language input, you determine what intents and entities your bot needs to recognize, and think about how they'll connect to actions that your bot takes. In [luis.ai](https://www.luis.ai), you define custom intents and entities and you specify their behavior by providing examples for each intent and labeling the entities within them. +## Language understanding -Your bot uses the intent recognized by LUIS to determine the conversation topic, or begin a conversation flow. For example, when a user says "I'd like to book a flight", your bot detects the BookFlight intent and invokes the conversation flow for starting a search for flights. LUIS detects entities like the destination city and the departure date, both in the original utterance that triggers the intent and later in the conversation flow. Once the bot has all the information it needs, it can fulfill the user's intent. +Natural language understanding features let you build custom natural language understanding models to predict the overall intention of user's message and extract important information from it. -![A conversation with a bot is triggered by the BookFlight intent](media/cognitive-services-add-bot-language/cognitive-services-luis-conversation-high-level.png) +| Service or feature | Description | +|:-|:-| +| Conversational Language Understanding (CLU) | A feature of the Azure AI Language service. | +| Language Understanding (LUIS) | An Azure AI service. (CLU is an updated version of LUIS.)

LUIS will be retired on 1 October 2025. | -### Recognize intent in common scenarios +### Conversational Language Understanding (CLU) -To save development time, LUIS provides pre-trained language models that recognize common utterances for common categories of bots. +Conversational language understanding (CLU) enables users to build custom natural language understanding models to predict the overall intention of an incoming utterance and extract important information from it. CLU only provides the intelligence to understand the input text for the client application and doesn't perform any actions on its own. -**Prebuilt domains** are pre-trained, ready-to-use collections of intents and entities that work well together for common scenarios like appointments, reminders, management, fitness, entertainment, communication, reservations, and more. The **Utilities** prebuilt domain helps your bot handle common tasks like Cancel, Confirm, Help, Repeat, and Stop. Take a look at the [prebuilt domains](https://docs.microsoft.com/azure/cognitive-services/LUIS/luis-how-to-use-prebuilt-domains) that LUIS offers. +To use CLU in your bot, create a language resource and a conversation project, train and deploy your language model, and then implement in your bot a _telemetry recognizer_ that forwards requests to the CLU API. -**Prebuilt entities** help your bot recognize common types of information like dates, times, numbers, temperature, currency, geography, and age. See [use prebuilt entities](https://docs.microsoft.com/azure/cognitive-services/LUIS/pre-builtentities) for background on the types that LUIS can recognize. +For more information, see: -## How your bot gets messages from LUIS +- [What is conversational language understanding?](/azure/ai-services/language-service/conversational-language-understanding/overview) +- _Telemetry recognizer_ interface reference for [C#/.NET](/dotnet/api/microsoft.bot.builder.ai.luis.itelemetryrecognizer) or [JavaScript/node.js](/javascript/api/botbuilder-ai/luisrecognizertelemetryclient) +- [Azure Cognitive Language Services Conversations client library for .NET](/dotnet/api/overview/azure/ai.language.conversations-readme) -Once you have set up and connected LUIS, your bot can send the message to your LUIS app, which returns a JSON response that contains the intents and entities. Then, you can use the [turn context](~/v4sdk/bot-builder-basics.md#defining-a-turn) in your bot's _turn handler_ to route the conversation flow based on the intent in the LUIS response. +### Language Understanding (LUIS) -![How intents and entities are passed to your bot](./media/cognitive-services-add-bot-language/cognitive-services-luis-message-flow-bot-code.png) +> [!NOTE] +> [Language Understanding (LUIS) will be retired on 1 October 2025](https://azure.microsoft.com/updates/language-understanding-retirement/). +> Beginning 1 April 2023, you won't be able to create new LUIS resources. -To get started using a LUIS app with your bot, check out [using LUIS for language understanding](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0). +LUIS applies custom machine-learning intelligence to a user's conversational, natural language text to predict overall meaning, and pull out relevant, detailed information. -## Best practices for Language Understanding +To use LUIS in your bot, create, train, and publish a LUIS app, then add a _LUIS recognizer_ to your bot. -Consider the following practices when designing a language model for your bot. +For more information, see: -### Consider the number of intents +- [What is Language Understanding (LUIS)?](/azure/ai-services/luis/what-is-luis) +- [Add natural language understanding to your bot](bot-builder-howto-v4-luis.md) -LUIS apps recognize intent by classifying an utterance into one of multiple categories. A natural result is that determining the correct category from among a large number of intents can reduce a LUIS app's ability to distinguish between them. +## Questions and answers -One way of reducing the number of intents is to use a hierarchical design. Consider the case of a personal assistant bot that has three intents related to weather, three intents related to home automation, and three other utility intents which are Help, Cancel and Greeting. If you put all the intents in the same LUIS app, you already have 9, and as you add features to the bot, you could end up with dozens. Instead, you can use a dispatcher LUIS app to determine whether the user's request is for weather, home automation, or utility, then call the LUIS app for the category that the dispatcher determines. In this case each of the LUIS apps only starts with 3 intents. +Question-and-answer features let you build knowledge bases to answer user questions. +Knowledge bases represent semi-structured content, such as that found in FAQs, manuals, and documents. -### Use a None intent +| Service or feature | Description | +|:-|:-| +| Question answering | A feature of the Azure AI Language service. | +| QnA Maker | An Azure AI services service. (Question answering is an updated version of QnA Maker.)

Azure AI QnA Maker will be retired on 31 March 2025. | -It's often the case that users of your bot will say something unexpected or unrelated to the current conversation flow. The _None_ intent is provided for handling those messages. +### Question answering -If you don't train an intent for handling the fallback, default or "none of the above" cases, your LUIS app can only classify messages into the intents it has defined. So for example, let's say you have a LUIS app with two intents: `HomeAutomation.TurnOn` and `HomeAutomation.TurnOff`. If those are the only intents, and the input is something unrelated like "schedule an appointment on Friday", your LUIS app has no choice but to classify that message as either HomeAutomation.TurnOn or HomeAutomation.TurnOff. If your LUIS app has a `None` intent with a few examples, you can provide some fallback logic in your bot to handle unexpected utterances. +Question answering provides cloud-based natural language processing (NLP) that allows you to create a natural conversational layer over your data. It's used to find the most appropriate answer for any input from your custom knowledge base of information. -The `None` intent is very useful for improving recognition results. In this home automation scenario, "schedule an appointment on Friday" may produce the `HomeAutomation.TurnOn` intent with a low confidence, and your bot should reject it. You can add such phrases to your model under the `None` intent, so that they resolve correctly to `None`. +To use question answering in your bot, create and deploy a question answering project, then implement in your bot a _QnA Maker client_ that forwards requests to the question answering API. -### Review the utterances that LUIS app receives +For more information, see: -LUIS apps provide a feature for improving your app performance, by reviewing messages that users sent to it. See [suggested utterances](https://docs.microsoft.com/azure/cognitive-services/LUIS/label-suggested-utterances) for a step-by-step walkthrough. +- [Use question answering to answer questions](../bot-builder-howto-answer-questions.md) +- [What is question answering?](/azure/ai-services/language-service/question-answering/overview) +- _QnA Maker client_ interface reference for [C#/.NET](/dotnet/api/microsoft.bot.builder.ai.qna.iqnamakerclient) or [JavaScript/node.js](/javascript/api/botbuilder-ai/qnamakerclient) +- [Azure Cognitive Language Services Question Answering client library for .NET](/dotnet/api/overview/azure/ai.language.questionanswering-readme) -## Integrate multiple LUIS apps and QnA services with the Dispatch tool +### QnA Maker -When building a multi-purpose bot that understands multiple conversational topics, you can start to develop services for each function separately, and then integrate them together. These services can include Language Understanding (LUIS) apps and QnAMaker services. Here are a few example scenarios in which a bot might combine multiple LUIS apps, multiple QnAMaker services or a combination of the two: +> [!NOTE] +> [Azure AI QnA Maker will be retired on 31 March 2025](https://azure.microsoft.com/updates/azure-qna-maker-will-be-retired-on-31-march-2025/). +> Beginning 1 October 2022, you won't be able to create new QnA Maker resources or knowledge bases. -* A personal assistant bot lets the user invoke a variety of commands. Each category of commands form a "skill" that can be developed separately, and each skill has a LUIS app. -* A bot searches many knowledge bases to find answers to frequently asked questions (FAQs). -* A bot for a business has LUIS apps for creating customer accounts and placing orders, and also has a QnAMaker service for its FAQ. +QnA Maker has the built-in ability to scrape questions and answers from an existing FAQ site, plus it also allows you to manually configure your own custom list of questions and answers. +QnA Maker has natural language processing abilities, enabling it to even provide answers to questions that are worded slightly differently than expected. +However, it doesn't have semantic language understanding abilities, so it can't determine that a puppy is a type of dog, for example. -### The Dispatch tool +To use QnA Maker in your bot, create a QnA Maker service, publish your knowledge base, and add a _QnA Maker_ object to your bot. -The Dispatch tool helps you integrate multiple LUIS apps and QnA Maker services with your bot, by creating a *dispatch app*, which is a new LUIS app that routes messages to the appropriate LUIS and QnAMaker services. See the [dispatch tutorial](./bot-builder-tutorial-dispatch.md) for a step-by-step tutorial that combines multiple LUIS apps and QnA Maker in one bot. +For more information, see: -## Use LUIS to improve speech recognition +- [What is QnA Maker?](/azure/ai-services/qnamaker/overview/overview) +- [Use QnA Maker to answer questions](bot-builder-howto-qna.md) -For a bot that users will speak to, integrating it with LUIS can help your bot identify words that might be misunderstood when converting speech to text. For example, in a chess scenario, a user might say: "Move knight to A 7". Without context for the user's intent, the utterance might be recognized as: "Move night 287". By creating entities that represent chess pieces and coordinates and labeling them in utterances, you provide context for speech recognition to identify them. You can [enable speech recognition priming](https://docs.microsoft.com/azure/bot-service/bot-service-manage-speech-priming?view=azure-bot-service-4.0) with Bot Framework channels that are integrated with Bing Speech, such as Web Chat, the Bot Framework Emulator and Cortana. +## Search + +Azure Cognitive Search helps your bot provide users with a rich search experience, including the ability to facet and filter information. + +- You can use Azure Cognitive Search as a feature within Azure AI Language. +- You can use the Azure Cognitive Search service directly. + +### Azure Cognitive Search + +You can use [Azure Cognitive Search](/azure/search/) to create an efficient index with which to search, facet, and filter a data store. + +- For how to configure Cognitive Search within Azure AI Language, see [Configure custom question answering enabled resources](/azure/ai-services/language-service/question-answering/how-to/configure-resources). +- For information about the Cognitive Search service, see [What is Azure Cognitive Search?](/azure/search/search-what-is-azure-search). + +## Use multiple features together + +To build a multi-purpose bot that understands multiple conversational topics, begin with support for each function separately, and then integrate them together. +Scenarios in which a bot might combine multiple features include: + +- A bot that provides a set of features, where each feature has its own language model. +- A bot that searches multiple knowledge bases to find answers to a user's questions. +- A bot that integrates different types of features, such as language understanding, answering questions, and search. + +This table describes different ways you can integrate multiple features. + +| Service or feature | Description | +|:-|:-| +| Orchestration workflow | A feature of the Azure AI Language service that allows you to use multiple question answering, CLU, and LUIS projects together. | +| Bot Framework Orchestrator | An intent-only recognition engine, which you can use to determine which LUIS model or QnA Maker knowledge base can best handle a given message. | +| Custom | You can implement your own logic to decide how best to handle the user's request. | + +### Use orchestration workflow + +The orchestration workflow applies machine-learning intelligence to enable you to build orchestration models to connect conversational language understanding (CLU) components, question answering projects, and LUIS applications. + +To use the orchestration workflow in your bot, create an orchestration workflow project, build your schema, train and deploy your model, then query your model API for intent predictions. + +For more information, see: + +- [What is orchestration workflow?](/azure/ai-services/language-service/orchestration-workflow/overview) +- [Azure Cognitive Language Services Conversations client library for .NET](/dotnet/api/overview/azure/ai.language.conversations-readme) + +### Orchestrator + +> [!NOTE] +> [Azure AI QnA Maker will be retired on 31 March 2025](https://azure.microsoft.com/updates/azure-qna-maker-will-be-retired-on-31-march-2025/). +> Beginning 1 October 2022, you won't be able to create new QnA Maker resources or knowledge bases. +> +> [Language Understanding (LUIS) will be retired on 1 October 2025](https://azure.microsoft.com/updates/language-understanding-retirement/). +> Beginning 1 April 2023, you won't be able to create new LUIS resources. + +Bot Framework Orchestrator is an intent-only recognition engine. The Bot Framework CLI includes tools to generate a language model for Orchestrator from a collection of QnA Maker knowledge bases and LUIS language models. Your bot can then use Orchestrator to determine which service can best respond to the user's input. + +The Bot Framework SDK provides built-in support for LUIS and QnA Maker. This enables you to trigger dialogs or automatically answer questions using LUIS and QnA Maker with minimal configuration. + +For more information, see [Use multiple LUIS and QnA models with Orchestrator](bot-builder-tutorial-orchestrator.md). + +### Custom logic + +There are two main ways to implement your own logic: + +1. For each message, call all relevant services that your bot supports. Use the results from the service that has the best confidence score. If the best score is ambiguous, ask the user to choose which response they want. +1. Call each service in a preferred order. Use the first result that has a sufficient confidence score. + +> [!TIP] +> When implementing a combination of different service or feature types, test inputs with each of the tools to determine the threshold score for each of your models. The services and features use different scoring criteria, so the scores generated across these tools are not directly comparable. +> +> The LUIS and QnA Maker services normalize scores. So, one score can be _good_ in one LUIS model but not so good in another model. + +## Migrate existing language projects + +For information on migrating resources from older services to Azure AI Language, see: + +- [Migrate from LUIS, QnA Maker, and Text Analytics](/azure/ai-services/language-service/concepts/migrate) +- [Backwards compatibility with LUIS applications](/azure/ai-services/language-service/conversational-language-understanding/how-to/migrate-from-luis) +- [Migrate from QnA Maker to Question Answering](/azure/ai-services/language-service/question-answering/how-to/migrate-qnamaker-to-question-answering) +- [Migrate from QnA Maker to custom question answering](/azure/ai-services/language-service/question-answering/how-to/migrate-qnamaker) ## Additional resources -Refer to [Cognitive Services](https://docs.microsoft.com/azure/cognitive-services/) documentation for more information. + +To manage specific project or resources: + +- To manage of Azure resources, go to the [Azure portal](https://ms.portal.azure.com/). +- To manage Azure AI Language projects, go to the [Language Studio portal](https://language.cognitive.azure.com/home). + - [Conversational language understanding (CLU) projects](https://language.cognitive.azure.com/clu/projects) + - [Question answering projects](https://language.cognitive.azure.com/questionAnswering/projects) +- To manage LUIS apps, go to the [Language Understanding (LUIS) portal](https://www.luis.ai/). +- To manage QnA Maker knowledge bases, go to the [QnA Maker portal](https://www.qnamaker.ai/). + +For documentation for a specific feature or service: + +- [What is Azure AI Language?](/azure/ai-services/language-service/overview) + - [What is conversational language understanding?](/azure/ai-services/language-service/conversational-language-understanding/overview) + - [What is question answering?](/azure/ai-services/language-service/question-answering/overview) +- [What is Azure Cognitive Search?](/azure/search/search-what-is-azure-search) +- [What is Language Understanding (LUIS)?](/azure/ai-services/luis/what-is-luis) +- [What is QnA Maker?](/azure/ai-services/qnamaker/overview/overview) diff --git a/articles/v4sdk/bot-builder-concept-middleware.md b/articles/v4sdk/bot-builder-concept-middleware.md index 54dc5a004..9aa68a220 100644 --- a/articles/v4sdk/bot-builder-concept-middleware.md +++ b/articles/v4sdk/bot-builder-concept-middleware.md @@ -1,95 +1,105 @@ --- -title: Middleware - Bot Service -description: Understand middleware and it's uses within the bot SDK. +title: Middleware +description: Learn about middleware, the layer between adapters and bots. See what type of functionality to implement in middleware and learn about short circuiting. keywords: middleware, middleware pipeline, short circuit, middleware uses -author: ivorb -ms.author: kamrani -manager: kamrani +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Middleware -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] Middleware is simply a class that sits between the adapter and your bot logic, added to your adapter's middleware collection during initialization. The SDK allows you to write your own middleware or add middleware created by others. Every activity coming into or out of your bot flows through your middleware. -The adapter processes and directs incoming activities in through the bot middleware pipeline to your bot’s logic and then back out again. As each activity flows in and out of the bot, each piece of middleware can inspect or act upon the activity, both before and after the bot logic runs. +The adapter processes and directs incoming activities in through the bot middleware pipeline to your bot's logic and then back out again. As each activity flows in and out of the bot, each piece of middleware can inspect or act upon the activity, both before and after the bot logic runs. -Before jumping into middleware, it is important to understand [bots in general](~/v4sdk/bot-builder-basics.md) and [how they process activities](~/v4sdk/bot-builder-basics.md#the-activity-processing-stack). +Before jumping into middleware, it's important to understand [bots in general](~/v4sdk/bot-builder-basics.md) and [how they process activities](~/v4sdk/bot-builder-basics.md#the-activity-processing-stack). ## Uses for middleware -The question often comes up: "When should I implement actions as middleware versus using my normal bot logic?" Middleware provides you with additional opportunities to interact with your users' conversation flow both before and after each _turn_ of the conversation is processed. Middleware also allows you to store and retrieve information concerning the conversation and call additional processing logic when required. Below are some common scenarios that show where middleware can be useful. + +The question often comes up: "When should I implement actions as middleware versus using my normal bot logic?" Middleware provides you with extra opportunities to interact with your users' conversation flow both before and after each _turn_ of the conversation is processed. Middleware also allows you to store and retrieve information concerning the conversation and call additional processing logic when required. Below are some common scenarios that show where middleware can be useful. ### Looking at or acting on every activity -There are plenty of situations that require your bot to do something on every activity, or for every activity of a certain type. For example, you may want to log every message activity your bot receives or provide a fallback response if the bot has not otherwise generated a response this turn. Middleware is a great place for this, with its ability to act both before and after the rest of the bot logic has executed. + +There are plenty of situations that require your bot to do something on every activity, or for every activity of a certain type. For example, you may want to log every message activity your bot receives or provide a fallback response if the bot hasn't otherwise generated a response this turn. Middleware is a great place for such processes, with its ability to act both before and after the rest of the bot logic has executed. ### Modifying or enhancing the turn context -Certain conversations can be much more fruitful if the bot has more information than what is provided in the activity. Middleware in this case could look at the conversation state information it has so far, query an external data source, and append that to the [turn context](~/v4sdk/bot-builder-basics.md#defining-a-turn) object before passing execution on to the bot logic. + +Certain conversations can be much more fruitful if the bot has more information than what is provided in the activity. Middleware in this case could look at the conversation state information it has so far, query an external data source, and append that to the [turn context](~/v4sdk/bot-builder-basics.md#defining-a-turn) object before passing execution on to the bot logic. The SDK defines logging middleware that can record incoming and outgoing activities, but you can also define your own middleware. ## The bot middleware pipeline + For each activity, the adapter calls middleware in the order in which you added it. The adapter passes in the context object for the turn and a _next_ delegate, and the middleware calls the delegate to pass control to the next middleware in the pipeline. Middleware also has an opportunity to do things after the _next_ delegate returns before completing the method. You can think of it as each middleware object has the first-and-last chance to act with respect to the middleware objects that follow it in the pipeline. For example: -- 1st middleware object’s turn handler executes code before calling _next_. - - 2nd middleware object’s turn handler executes code before calling _next_. - - The bot’s turn handler executes and returns. - - 2nd middleware object’s turn handler executes any remaining code before returning. -- 1st middleware object’s turn handler executes any remaining code before returning. +- First middleware object's turn handler executes code before calling _next_. + - Second middleware object's turn handler executes code before calling _next_. + - The bot's turn handler executes and returns. + - Second middleware object's turn handler executes any remaining code before returning. +- First middleware object's turn handler executes any remaining code before returning. -If middleware doesn’t call the next delegate, the adapter does not call any of the subsequent middleware or bot turn handlers, and the pipeline short circuits. +If middleware doesn't call the next delegate, the adapter doesn't call any of the subsequent middleware or bot turn handlers, and the pipeline short circuits. Once the bot middleware pipeline completes, the turn is over, and the turn context goes out of scope. Middleware or the bot can generate responses and register response event handlers, but keep in mind that responses are handled in separate processes. ## Order of middleware + Since the order in which middleware is added determines the order in which the middleware processes an activity, it's important to decide the sequence that middleware should be added. > [!NOTE] > This is meant to give you a common pattern that works for most bots, but be sure to consider how each piece of middleware will interact with the others for your situation. -The first things in your middleware pipeline should likely be those that take care of the lowest-level tasks that are used every time. Examples include logging, exception handling, and translation. Ordering these can vary depending on your needs, such as whether you want the incoming message to be translated first, before messages are stored, or if message storage should occur first, which could mean stored messages wouldn't be translated. +Middleware that takes care of the lowest-level tasks that to every bot should be added to your middleware pipeline first. +Examples include logging, exception handling, and translation. Order these depending on your needs, such as whether you want the incoming message to be translated first, before messages are stored, or if message storage should occur first, which could mean stored messages wouldn't be translated. -The last things in your middleware pipeline should be bot-specific middleware, which is middleware you implement to do some processing on every message sent to your bot. If your middleware uses state information or other information set in the bot context, add it to the middleware pipeline after the middleware that modifies state or context. +Bot-specific middleware should be added to your middleware pipeline last, middleware you implement to do some processing on every message sent to your bot. If your middleware uses state information or other information set in the bot context, add it to the middleware pipeline after the middleware that modifies state or context. ## Short circuiting -An important idea around middleware and response handlers is _short circuiting_. If execution is to continue through the layers that follow it, middleware (or a response handler) is required to pass execution on by calling its _next_ delegate. If the next delegate is not called within that middleware (or response handler), the associated pipeline short circuits and subsequent layers are not executed. This means all bot logic, and any middleware further along the pipeline, is skipped. There is a subtle difference between your middleware and your response handler short circuiting a turn. -When middleware short circuits a turn, your bot turn handler will not be called, but all middleware code executed prior to this point in the pipeline will still run to completion. +An important idea around middleware and response handlers is _short circuiting_. If execution is to continue through the layers that follow it, middleware (or a response handler) is required to pass execution on by calling its _next_ delegate. If the next delegate isn't called within that middleware (or response handler), the associated pipeline short circuits and subsequent layers aren't executed. This means all bot logic, and any middleware further along the pipeline, is skipped. There's a subtle difference between your middleware and your response handler short circuiting a turn. -For event handlers, not calling _next_ means that the event is cancelled, which is a significantly different result than middleware skipping logic. By not processing the rest of the event, the adapter never sends it. +When middleware short circuits a turn, your bot turn handler won't be called, but all middleware code executed prior to this point in the pipeline will still run to completion. + +For event handlers, not calling _next_ means that the event is canceled, which is very different than middleware skipping logic. By not processing the rest of the event, the adapter never sends it. > [!TIP] > If you do short-circuit a response event, such as `SendActivities`, be sure it's the behavior you intend. Otherwise, it can result in difficult to fix bugs. ## Response event handlers + In addition to the application and middleware logic, response handlers (also sometimes referred to as event handlers, or activity event handlers) can be added to the context object. These handlers are called when the associated response happens on the current context object, before executing the actual response. These handlers are useful when you know you'll want to do something, either before or after the actual event, for every activity of that type for the rest of the current response. > [!WARNING] > Be careful to not call an activity response method from within its respective response event handler, for example, calling the send activity method from within an on send activity handler. Doing so can generate an infinite loop. Remember, each new activity gets a new thread to execute on. When the thread to process the activity is created, the list of handlers for that activity is copied to that new thread. No handlers added after that point will be executed for that specific activity event. -The handlers registered on a context object are handled very similarly to how the adapter manages the middleware pipeline. Namely, handlers get called in the order they're added, and calling the next delegate passes control to the next registered event handler. If a handler doesn’t call the next delegate, none of the subsequent event handlers are called, the event short circuits, and the adapter does not send the response to the channel. +The handlers registered on a context object are handled similarly to how the adapter manages the middleware pipeline. Namely, handlers get called in the order they're added, and calling the next delegate passes control to the next registered event handler. If a handler doesn't call the next delegate, none of the subsequent event handlers are called, the event short circuits, and the adapter doesn't send the response to the channel. ## Handling state in middleware -A common method to save state is to call the save changes method at the end of the turn handler. Here is a diagram with a focus on the call. +A common method to save state is to call the save changes method at the end of the turn handler. Here's a diagram with a focus on the call. -![state middleware issues](media/bot-builder-dialog-state-problem.png) +:::image type="content" source="media/bot-builder-dialog-state-problem.png" alt-text="Sequence diagram of a bot turn, with state saved from the bot's turn handler."::: -The problem with this approach is that any state updates made from some custom middleware that happens after the bot’s turn handler has returned will not be saved to durable storage. The solution is to move the call to the save changes method to after the custom middleware has completed by adding an instance of the _auto-save changes_ middleware to the beginning of the middleware stack, or at least before any of the middleware that might update state. The execution is shown below. +The problem with this approach is that any state updates made from some custom middleware that happens after the bot's turn handler has returned won't be saved to durable storage. The solution is to move the call to the save changes method to after the custom middleware has completed by adding an instance of the _auto-save changes_ middleware to the beginning of the middleware stack, or at least before any of the middleware that might update state. The execution is shown below. -![state middleware solution](media/bot-builder-dialog-state-solution.png) +:::image type="content" source="media/bot-builder-dialog-state-solution.png" alt-text="Sequence diagram of a bot turn, with state saved from middleware."::: Add the state management objects that will need updating to a _bot state set_ object, and then use that when you create your auto-save changes middleware. - ## Additional resources + You can take a look at the transcript logger middleware, as implemented in the Bot Framework SDK [[C#](https://github.com/Microsoft/botbuilder-dotnet/blob/master/libraries/Microsoft.Bot.Builder/TranscriptLoggerMiddleware.cs) | [JS](https://github.com/Microsoft/botbuilder-js/blob/master/libraries/botbuilder-core/src/transcriptLogger.ts)]. diff --git a/articles/v4sdk/bot-builder-concept-regionalization.md b/articles/v4sdk/bot-builder-concept-regionalization.md new file mode 100644 index 000000000..02a8b5213 --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-regionalization.md @@ -0,0 +1,44 @@ +--- +title: Regionalization support +description: Learn about regionalization in Azure AI Bot Service and how to meet your data compliance requirements. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Regionalization in Azure AI Bot Service + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Azure AI Bot Service is a global Azure service that allows bot developers in all regions to register their bot and connect it to different channels. This also lets developers meet compliance obligations, especially following the [Schrems II decision](https://blogs.microsoft.com/eupolicy/2021/05/06/eu-data-boundary/). + +Use an Azure Bot resource to register a bot with a regional Azure AI Bot Service. Adding regional settings to a bot ensures user personal data is preserved, stored, and processed within certain geographic boundaries (like EU boundaries, Indian boundaries). This article explains the areas of bot development impacted by regionalization and where to update settings to maintain compliance. + +## Deploy locally developed regional Azure bots + +Your bot can be hosted anywhere, even if you have a regional Azure Bot resource. To maintain complete end-to-end data residency, however, you should host your bot code in the same locality as your Azure Bot resource. Currently, regional bots are supported in Europen and India. For example, developers hosting bots in the European Union will want to ensure their bots are deployed in a region within EU geographical boundaries. + +For more information about deploying regionalized bots, see [Provision and publish a bot](../provision-and-publish-a-bot.md). + +## Register regional Azure bots + +When you create a bot in Azure, you can set its region to maintain data compliance. When you create a bot, make sure to create your resource in a geographically compliant region. For more information, see [Create an Azure Bot resource](abs-quickstart.md#create-the-resource). + +>[!NOTE] +> Bot data may go beyond geographical boundaries as bot end-to-end scenarios may depend on many services. +> The regional Azure AI Bot Service only supports data in Azure AI Bot Service. Other Azure services—such as Azure AI services—and third-party channels may not align with compliance obligation and run the risk of data leaving the geographical region. + +For guidance about reliability support in Azure AI Bot Service, see [What is reliability in Azure AI Bot Service](/azure/reliability/reliability-bot). + +## Add authentication to a regional Azure bot + +Sometimes a bot must access secured online resources on behalf of the user. [OAuth](bot-builder-concept-authentication.md) is used to authenticate the user and authorize the bot. + +- For information about which regions and clouds are supported, see [supported OAuth URLs](../ref-oauth-redirect-urls.md). +- For information on how to add user authentication, see [Add authentication to a bot](bot-builder-authentication.md). diff --git a/articles/v4sdk/bot-builder-concept-sso.md b/articles/v4sdk/bot-builder-concept-sso.md new file mode 100644 index 000000000..e37663b91 --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-sso.md @@ -0,0 +1,90 @@ +--- +title: Single sign-on in the Bot Framework SDK +description: Learn about single sign-on (SSO) to allow apps to share access to user resources. +keywords: Azure AI Bot Service, authentication, bot framework token service +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: article +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# About single sign-on + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Single sign-on (SSO) allows access to resources to be shared across independent applications. +For instance, a user could sign in to a service in a root bot, and the root bot could share the access token with a skill bot. +Currently, only the [Microsoft Entra ID](./bot-builder-concept-identity-providers.md#microsoft-entra-id-identity-provider) identity provider is supported. + +SSO applies to the following scenarios: + +- A root bot and one or more skill bots. The user signs in from the root bot. The bot then invokes multiple skills on behalf of the user. +- A Web Chat control embedded on a website. The user signs in from the website. The website then invokes a bot or a skill on behalf of the user. + +SSO provides the following advantages: + +- The user doesn't have to sign in multiple times. +- The root bot or website doesn't need to know the user's permissions. + +> [!NOTE] +> SSO is a available in Bot Framework SDK version 4.8 and later. + +## SSO components interaction + +The following time sequence diagrams show the interactions between the SSO various components. + +- The following diagram illustrates the flow for a root bot. + + :::image type="content" source="media/concept-bot-authentication/bot-auth-sso-va-time-sequence.PNG" alt-text="SSO sequence diagram for a root bot."::: + +- The following diagram illustrates the flow, and fallback flow, for a Web Chat control. + + :::image type="content" source="media/concept-bot-authentication/bot-auth-sso-webchat-time-sequence.PNG" alt-text="SSO sequence diagram for a Web Chat control."::: + + If the token exchange fails, the fall-back is to prompt the user to sign in. + Such failures can happen when extra permissions are required or the token is for the wrong service. + +Let's analyze the flow. + +1. The client starts a conversation with the bot triggering an OAuth scenario. +1. The bot sends back an OAuth Card to the client. +1. The client intercepts the OAuth card before displaying it to the user and checks if it contains a `TokenExchangeResource` property. +1. If the property exists, the client sends a `TokenExchangeInvokeRequest` to the bot. The client must have an exchangeable token for the user, which must be an Microsoft Entra ID token and whose audience must be the same as `TokenExchangeResource.Uri` property. The client sends an Invoke activity to the bot with the body shown below. + + ```json + { + "type": "Invoke", + "name": "signin/tokenExchange", + "value": { + "id": "", + "connectionName": "", + "token": "" + } + } + ``` + +1. The bot processes the `TokenExchangeInvokeRequest` and returns a `TokenExchangeInvokeResponse` back to the client. The +client should wait until it receives the `TokenExchangeInvokeResponse`. + + ```json + { + "status": "", + "body": { + "id":"", + "connectionName": "", + "failureDetail": "" + } + } + ``` + +1. If the `TokenExchangeInvokeResponse` has a `status` of `200`, then the client doesn't show the OAuth card. See the _normal flow_ diagram. For any other `status` or if the `TokenExchangeInvokeResponse` isn't received, then the client shows the OAuth card to the user. See the _fallback flow_ diagram. This ensures that the SSO flow falls back to the normal OAuthCard flow, if there are any errors or unmet dependencies, like user consent. + +## Next steps + +> [!div class="nextstepaction"] +> [Add single sign-on to a bot](bot-builder-authentication-sso.md) diff --git a/articles/v4sdk/bot-builder-concept-state.md b/articles/v4sdk/bot-builder-concept-state.md index 243cb0df2..57bf0dffc 100644 --- a/articles/v4sdk/bot-builder-concept-state.md +++ b/articles/v4sdk/bot-builder-concept-state.md @@ -1,49 +1,58 @@ --- -title: Managing State - Bot Service -description: Describes how state works within the Bot Framework SDK. +title: Managing state in Bot Framework SDK +description: Learn how bots handle user information, including the storage layer, state management, and state property accessors. keywords: state, bot state, conversation state, user state -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Managing state -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] State within a bot follows the same paradigms as modern web applications, and the Bot Framework SDK provides some abstractions to make state management easier. -As with web apps, a bot is inherently stateless; a different instance of your bot may handle any given turn of the conversation. For some bots, this simplicity is preferred - the bot can either operate without additional information, or the information required is guaranteed to be within the incoming message. For others, state (such as where in the conversation we are or previously received data about the user) is necessary for the bot to have a useful conversation. +As with web apps, a bot is inherently stateless; a different instance of your bot may handle any given turn of the conversation. For some bots, this simplicity is preferred—the bot can either operate without additional information, or the information required is guaranteed to be within the incoming message. For others, state (such as where the conversation left off or data previously received about the user) is necessary for the bot to have a useful conversation. **Why do I need state?** Maintaining state allows your bot to have more meaningful conversations by remembering certain things about a user or conversation. For example, if you've talked to a user previously, you can save previous information about them, so that you don't have to ask for it again. State also keeps data for longer than the current turn, so that your bot keeps information over the course of a multi-turn conversation. -As it pertains to bots, there are a few layers to using state which we'll cover here: the storage layer, state management (contained in the bot state in the diagram below), and state property accessors. This diagram illustrates parts of the interaction sequence between these layers, with the solid arrows representing a method call, and the dashed arrows representing the response (with or without a return value). +As it pertains to bots, there are a few layers to using state: the storage layer, state management (contained in the bot state in the diagram below), and state property accessors. This diagram illustrates parts of the interaction sequence between these layers, with the solid arrows representing a method call, and the dashed arrows representing the response (with or without a return value). -![bot state](media/bot-builder-state.png) +:::image type="content" source="media/bot-builder-state.png" alt-text="Sequence diagram illustrating how state is loaded, cached, and stored each turn."::: The flow of this diagram is explained in following sections with details each of these layers. ## Storage layer -Starting at the backend, where the state information is actually stored, is our *storage layer*. This can be thought of as our physical storage, such as in-memory, Azure, or a third party server. +Starting at the backend, where the state information is actually stored, is the _storage layer_. This can be thought of as your physical storage, such as in-memory, Azure, or a third party server. The Bot Framework SDK includes some implementations for the storage layer: - **Memory storage** implements in-memory storage for testing purposes. In-memory data storage is intended for local testing only as this storage is volatile and temporary. The data is cleared each time the bot is restarted. - **Azure Blob Storage** connects to an Azure Blob Storage object database. -- **Azure Cosmos DB storage** connects to a Cosmos DB NoSQL database. +- **Azure Cosmos DB partitioned storage** connects to a partitioned Cosmos DB NoSQL database. + +>[!IMPORTANT] +> The _Cosmos DB storage_ class has been deprecated. Containers originally created with CosmosDbStorage had no partition key set, and were given the default partition key of "\/_partitionKey". +> +> Containers created with _Cosmos DB storage_ can be used with _Cosmos DB partitioned storage_. Read [Partitioning in Azure Cosmos DB](/azure/cosmos-db/partitioning-overview) for more information. +> +> Also note that, unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage doesn't automatically create a database within your Cosmos DB account. You need to [create a new database manually](/azure/cosmos-db/create-cosmosdb-resources-portal), but skip manually creating a container since _CosmosDbPartitionedStorage_ will create the container for you. For instructions on how to connect to other storage options, see [write directly to storage](bot-builder-howto-v4-storage.md). ## State management -*State management* automates the reading and writing of your bot's state to the underlying storage layer. State is stored as *state properties*, which are effectively key-value pairs that your bot can read and write through the state management object without worrying about the specific underlying implementation. Those state properties define how that information is stored. For example, when you retrieve a property that you defined as a specific class or object, you know how that data will be structured. +_State management_ automates the reading and writing of your bot's state to the underlying storage layer. State is stored as _state properties_, which are effectively key-value pairs that your bot can read and write through the state management object without worrying about the specific underlying implementation. Those state properties define how that information is stored. For example, when you retrieve a property that you defined as a specific class or object, you know how that data will be structured. These state properties are lumped into scoped "buckets", which are just collections to help organize those properties. The SDK includes three of these "buckets": @@ -51,23 +60,23 @@ These state properties are lumped into scoped "buckets", which are just collecti - Conversation state - Private conversation state -All of these buckets are subclasses of the *bot state* class, which can be derived to define other types of buckets with different scopes. +All of these buckets are subclasses of the _bot state_ class, which can be derived to define other types of buckets with different scopes. These predefined buckets are scoped to a certain visibility, depending on the bucket: - User state is available in any turn that the bot is conversing with that user on that channel, regardless of the conversation -- Conversation state is available in any turn in a specific conversation, regardless of user (i.e. group conversations) +- Conversation state is available in any turn in a specific conversation, regardless of user, such as in group conversations - Private conversation state is scoped to both the specific conversation and to that specific user > [!TIP] > Both user and conversation state are scoped by channel. > The same person using different channels to access your bot appears as different users, one for each channel, and each with a distinct user state. -The keys used for each of these predefined buckets are specific to the user and conversation, or both. When setting the value of your state property, the key is defined for you internally with information contained on the turn context to ensure that each user or conversation gets placed in the correct bucket and property. Specifically, the keys are defined as follows: +The keys used for each of these predefined buckets are specific to the user and conversation, or both. When setting the value of your state property, the key is defined for you internally, with information contained on the turn context to ensure that each user or conversation gets placed in the correct bucket and property. Specifically, the keys are defined as follows: -- The user state creates a key using the *channel ID* and *from ID*. For example, _{Activity.ChannelId}/users/{Activity.From.Id}#YourPropertyName_ -- The conversation state creates a key using the *channel ID* and the *conversation ID*. For example, _{Activity.ChannelId}/conversations/{Activity.Conversation.Id}#YourPropertyName_ -- The private conversation state creates a key using the *channel ID*, *from ID* and the *conversation ID*. For example, _{Activity.ChannelId}/conversations/{Activity.Conversation.Id}/users/{Activity.From.Id}#YourPropertyName_ +- The user state creates a key using the _channel ID_ and _from ID_. For example, _{Activity.ChannelId}/users/{Activity.From.Id}#YourPropertyName_ +- The conversation state creates a key using the _channel ID_ and the _conversation ID_. For example, _{Activity.ChannelId}/conversations/{Activity.Conversation.Id}#YourPropertyName_ +- The private conversation state creates a key using the _channel ID_, _from ID_ and the _conversation ID_. For example, _{Activity.ChannelId}/conversations/{Activity.Conversation.Id}/users/{Activity.From.Id}#YourPropertyName_ ### When to use each type of state @@ -92,40 +101,53 @@ For details on using these predefined buckets, see the [state how-to article](bo ### Connecting to multiple databases If your bot needs to connect to multiple databases, create a storage layer for each database. +You might choose to use multiple databases if your bot collects information that has different security, concurrency, or data location needs. + For each storage layer, create the state management objects you need to support your state properties. ## State property accessors -*State property accessors* are used to actually read or write one of your state properties, and provide *get*, *set*, and *delete* methods for accessing your state properties from within a turn. To create an accessor, you must provide the property name, which usually takes place when you're initializing your bot. Then, you can use that accessor to get and manipulate that property of your bot's state. +_State property accessors_ are used to actually read or write one of your state properties, and provide _get_, _set_, and _delete_ methods for accessing your state properties from within a turn. To create an accessor, you must provide the property name, which usually takes place when you're initializing your bot. Then, you can use that accessor to get and manipulate that property of your bot's state. -The accessors allow the SDK to get state from the underlying storage, and update the bot's *state cache* for you. The state cache is a local cache maintained by your bot that stores the state object for you, allowing read and write operations without accessing the underlying storage. If it isn't already in the cache, calling the accessor's *get* method retrieves state and also places it in the cache. Once retrieved, the state property can be manipulated just like a local variable. +The accessors allow the SDK to get state from the underlying storage, and update the bot's _state cache_ for you. The state cache is a local cache maintained by your bot that stores the state object for you, allowing read and write operations without accessing the underlying storage. If it isn't already in the cache, calling the accessor's _get_ method retrieves state and also places it in the cache. Once retrieved, the state property can be manipulated just like a local variable. -The accessor's *delete* method removes the property from the cache, and also deletes it from the underlying storage. +The accessor's _delete_ method removes the property from the cache, and also deletes it from the underlying storage. > [!IMPORTANT] -> For the first call to an accessor's *get* method, you must provide a factory method to create the object if it doesn't yet exist in your state. If no factory method is given, you will get an exception. Details on how to use a factory method can be found in the [state how-to article](bot-builder-howto-v4-state.md). +> For the first call to an accessor's _get_ method, you must provide a factory method to create the object if it doesn't yet exist in your state. If no factory method is given, you'll get an exception. Details on how to use a factory method can be found in the [state how-to article](bot-builder-howto-v4-state.md). -To persist any changes you make to the state property you get from the accessor, the property in the state cache must be updated. You can do so via a call the accessors *set* method, which sets the value of your property in the cache, and is available if that needs to be read or updated later in that turn. To actually persist that data to the underlying storage (and thus make it available after the current turn), you must then [save your state](#saving-state). +To persist any changes you make to the state property you get from the accessor, the property in the state cache must be updated. You can do so via a call the accessors _set_ method, which sets the value of your property in the cache, and is available if that needs to be read or updated later in that turn. To actually persist that data to the underlying storage (and thus make it available after the current turn), you must then [save your state](#saving-state). ### How the state property accessor methods work The accessor methods are the primary way for your bot to interact with state. How each work, and how the underlying layers interact, are as follows: -- The accessor's *get* method: +- The accessor's _get_ method: - Accessor requests property from the state cache. - If the property is in the cache, return it. Otherwise, get it from the state management object. - (If it is not yet in state, use the factory method provided in the accessors *get* call.) -- The accessor's *set* method: + (If it's not yet in state, use the factory method provided in the accessors _get_ call.) +- The accessor's _set_ method: - Update the state cache with the new property value. -- The state management object's *save changes* method: +- The state management object's _save changes_ method: - Check the changes to the property in the state cache. - Write that property to storage. +## State in dialogs + +The dialogs library uses a dialog state property accessor, defined on the bot's conversation state, to retain a dialog's place in the conversation. The dialog state property also allows each dialog to store transient information in between turns. + +Adaptive dialogs have a more elaborate memory scope structure, which makes it easier to access configuration and recognition results, among other things. The _dialog manager_ uses the user and conversation state management objects to provide these memory scopes. + +For information about the dialogs library, see the [dialogs library](bot-builder-concept-dialog.md) article. + +- See [about component and waterfall dialogs](bot-builder-concept-waterfall-dialogs.md) for information specific to those types of dialogs. +- See the [introduction to adaptive dialogs](bot-builder-adaptive-dialog-introduction.md) and [managing state in adaptive dialogs](bot-builder-concept-adaptive-dialog-memory-states.md) articles for information specific to adaptive dialogs. + ## Saving state -When you call the accessor's set method to record the updated state, that state property has not yet been saved to your persisted storage, and instead is only saved to your bot's state cache. To save any changes in the state cache to your persisted state, you must call the state management object's *save changes* method, which is available on the implementation of the bot state class mentioned above (such as user state or conversation state). +When you call the accessor's set method to record the updated state, that state property has not yet been saved to your persisted storage, and instead is only saved to your bot's state cache. To save any changes in the state cache to your persisted state, you must call the state management object's _save changes_ method, which is available on the implementation of the bot state class mentioned above (such as user state or conversation state). -Calling the save changes method for a state management object (i.e. the buckets mentioned above) saves all properties in the state cache that you have set up to that point for that bucket, but not for any of the other buckets you may have in your bot's state. +Calling the save changes method for a state management object (such as the buckets mentioned above) saves all properties in the state cache that you've set up to that point for that bucket, but not for any of the other buckets you may have in your bot's state. > [!TIP] > Bot state implements a "last write wins" behavior, where the last write will stamp over the previously written state. This may work for many applications but has implications, particularly in scaled-out scenarios, where there may be some level of concurrency or latency in play. @@ -134,6 +156,5 @@ If you have some custom middleware that might update state after your turn handl ## Additional resources -- [Dialog state](bot-builder-concept-dialog.md#dialog-state) - [Write directly to storage](bot-builder-howto-v4-storage.md) - [Save conversation and user data](bot-builder-howto-v4-state.md) diff --git a/articles/v4sdk/bot-builder-concept-waterfall-dialogs.md b/articles/v4sdk/bot-builder-concept-waterfall-dialogs.md new file mode 100644 index 000000000..9cbf2d397 --- /dev/null +++ b/articles/v4sdk/bot-builder-concept-waterfall-dialogs.md @@ -0,0 +1,192 @@ +--- +title: About component and waterfall dialogs +description: Describes what component, waterfall, and prompt dialogs are and how they work within the Bot Framework SDK. +keywords: conversation flow, bot conversation, component dialog, waterfall dialog, prompt dialog, dialog set +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# About component and waterfall dialogs + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Dialogs come in a few different types. This article describes component, waterfall, and prompt dialogs. +For information about dialogs in general, see the [dialogs library](bot-builder-concept-dialog.md) article. For information about adaptive dialogs, see the [introduction to adaptive dialogs](bot-builder-adaptive-dialog-introduction.md). + +A _waterfall dialog_ (or waterfall) defines a sequence of steps, allowing your bot to guide a user through a linear process. +These dialogs are designed to work within the context of a _component dialog_. + +A component dialog is a type of container dialog that allows dialogs in the set to call other dialogs in the set, such as a waterfall dialog calling prompt dialogs or another waterfall dialog. +Component dialogs manage a set of _child_ dialogs, such as waterfall dialogs, prompts, and so on. +You can design a component dialog to handle specific tasks and reuse it, in the same bot or across multiple bots. + +_Prompt dialogs_ (prompts) are dialogs designed to ask the user for specific types of information, such as a number, a date, or a name, and so on. +Prompts are designed to work with waterfall dialogs in a component dialog. + +## Component dialogs + +Sometimes you want to write a reusable dialog that you want to use in different scenarios, such as an address dialog that asks the user to provide values for street, city and zip code. + +The _component dialog_ provides a strategy for creating independent dialogs to handle specific scenarios, breaking a large dialog set into more manageable pieces. Each of these pieces has its own dialog set, and avoids any name collisions with the dialog set that contains it. For more information, see the [component dialog how to](bot-builder-compositcontrol.md). + +## Waterfall dialogs + +A waterfall dialog is a specific implementation of a dialog that is commonly used to collect information from the user or guide the user through a series of tasks. Each step of the conversation is implemented as an asynchronous function that takes a _waterfall step context_ (`step`) parameter. At each step, the bot [prompts the user for input](bot-builder-prompts.md) (or can begin a child dialog, but that it's often a prompt), waits for a response, and then passes the result to the next step. The result of the first function is passed as an argument into the next function, and so on. + +The following diagram shows a sequence of waterfall steps and the stack operations that take place. Details on the use of the dialog stack are below in the [using dialogs](#using-dialogs) section. + +:::image type="content" source="media/bot-builder-dialog-concept.png" alt-text="Representation of how messages map to waterfall steps."::: + +Within waterfall steps, the context of the waterfall dialog is stored in its _waterfall step context_. The step context is similar to the dialog context and provides access to the current turn context and state. Use the waterfall step context object to interact with a dialog set from within a waterfall step. + +You can handle a return value from a dialog either within a waterfall step in a dialog or from your bot's on turn handler, although you generally only need to check the status of the dialog turn result from your bot's turn logic. +Within a waterfall step, the dialog provides the return value in the waterfall step context's _result_ property. + +### Waterfall step context properties + +The waterfall step context contains the following properties: + +- _Options_: contains input information for the dialog. +- _Values_: contains information you can add to the context, and is carried forward into subsequent steps. +- _Result_: contains the result from the previous step. + +Additionally, the _next_ method (**NextAsync** in C#, **next** in JavaScript and Python) continues to the next step of the waterfall dialog within the same turn, enabling your bot to skip a certain step if needed. + +## Prompts + +Prompts, within the dialogs library, provide an easy way to ask the user for information and evaluate their response. For example for a _number prompt_, you specify the question or information you're asking for, and the prompt automatically checks to see if it received a valid number response. If it did, the conversation can continue; if it didn't, it will reprompt the user for a valid answer. + +Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or starts from the top with a reprompt. + +Prompts have _prompt options_ given when the prompt is called, which is where you can specify the text to prompt with, the retry prompt if validation fails, and choices to answer the prompt. In general, the prompt and retry prompt properties are activities, though there's some variation on how this is handled in different programming languages. + +Additionally, you can choose to add some custom validation for your prompt when you create it. For example, say we wanted to get a party size using the number prompt, but that party size has to be more than 2 and less than 12. The prompt first checks to see if it received a valid number, then runs the custom validation if it's provided. If the custom validation fails, it will reprompt the user as above. + +When a prompt completes, it explicitly returns the resulting value that was asked for. When that value is returned, we can be sure it has passed both the built-in prompt validation and any additional custom validation that may have been provided. + +For examples on using various prompts, take a look at how to use the [dialogs library to gather user input](bot-builder-prompts.md). + +### Prompt types + +Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or restarts from the top with a reprompt. The dialogs library offers various basic prompts, each used for collecting a different type of response. The basic prompts can interpret natural language input, such as "ten" or "a dozen" for a number, or "tomorrow" or "Friday at 10am" for a date-time. + +| Prompt | Description | Returns | +|:----|:----|:----| +| _Attachment prompt_ | Asks for one or more attachments, such as a document or image. | A collection of _attachment_ objects. | +| _Choice prompt_ | Asks for a choice from a set of options. | A _found choice_ object. | +| _Confirm prompt_ | Asks for a confirmation. | A Boolean value. | +| _Date-time prompt_ | Asks for a date-time. | A collection of _date-time resolution_ objects. | +| _Number prompt_ | Asks for a number. | A numeric value. | +| _Text prompt_ | Asks for general text input. | A string. | + +To prompt a user for input, define a prompt using one of the built-in classes, such as the _text prompt_, and add it to your dialog set. Prompts have fixed IDs that must be unique within a dialog set. You can have a custom validator for each prompt, and for some prompts, you can specify a _default locale_. + +### Prompt locale + +The locale is used to determine language-specific behavior of the _choice_, _confirm_, _date-time_, and _number_ prompts. For any given input from the user, if the channel provided a _locale_ property in user's message, then that is used. Otherwise, if the prompt's _default locale_ is set, by providing it when calling the prompt's constructor or by setting it later, then that is used. If neither of those locales are provided, English ("en-us") is used as the locale. + +The locale is a two, three, or four character ISO 639 code that represents a language or language family. + +### Prompt options + +The second parameter of the step context's _prompt_ method takes a _prompt options_ object, which has the following properties. + +| Property | Description | +| :--- | :--- | +| _Prompt_ | The initial activity to send the user, to ask for their input. | +| _Retry prompt_ | The activity to send the user if their first input didn't validate. | +| _Choices_ | A list of choices for the user to choose from, for use with a choice prompt. | +| _Validations_ | Additional parameters to use with a custom validator. | +| _Style_ | Defines how the choices for a choice prompt or confirm prompt will be presented to a user. | + +You should always specify the initial prompt activity to send to the user, and a retry prompt for instances when the user's input doesn't validate. + +If the user's input isn't valid, the retry prompt is sent to the user; if there was no retry specified, then the initial prompt is used. However, if an activity is sent back to the user from within the validator, no retry prompt is sent. + +#### Prompt validation + +You can validate a prompt response before returning the value to the next step of the waterfall. A validator function has a _prompt validator context_ parameter and returns a Boolean, indicating whether the input passes validation. +The prompt validator context includes the following properties: + +| Property | Description | +| :--- | :--- | +| _Context_ | The current turn context for the bot. | +| _Recognized_ | A _prompt recognizer result_ that contains information about the user input, as processed by the recognizer. | +| _Options_ | Contains the _prompt options_ that were provided in the call to start the prompt. | + +The prompt recognizer result has the following properties: + +| Property | Description | +| :--- | :--- | +| _Succeeded_ | Indicates whether the recognizer was able to parse the input. | +| _Value_ | The return value from the recognizer. If necessary, the validation code can modify this value. | + +## Using dialogs + +Dialogs can be thought of as a programmatic stack, which we call the _dialog stack_, with the turn handler as the one directing it and serving as the fallback if the stack is empty. The top-most item on that stack is considered the _active dialog_, and the dialog context directs all input to the active dialog. + +When a dialog begins, it's pushed onto the stack, and is now the active dialog. It remains the active dialog until it either ends, it's removed by the [replace dialog](#repeating-a-dialog) method, or another dialog is pushed onto the stack (by either the turn handler or active dialog itself) and becomes the active dialog. When that new dialog ends, it's popped off the stack and the next dialog down becomes the active dialog again. This allows for [repeating a dialog](#repeating-a-dialog) or [branching a conversation](#branch-a-conversation), discussed below. + +You can begin or continue a root dialog using the _run_ dialog extension method. From the bot code, calling the dialog run extension method either continues the existing dialog, or starts a new instance of the dialog if the stack is currently empty. Control and user input goes to the active dialog on the stack. + +The run method requires a _state property accessor_ to access the dialog state. The accessor is created and used the same way as other state accessors, but is created as it's own property based off of the conversation state. Details on managing state can be found in the [managing state topic](bot-builder-concept-state.md), and usage of dialog state is shown in the [sequential conversation flow](bot-builder-dialog-manage-conversation-flow.md) how-to. + +From within a dialog, you have access to the dialog context and can use it to start other dialogs, end the current dialog, and perform other operations. + +### To start a dialog + +From within a waterfall dialog, pass the _dialog ID_ of the dialog you want to start into the waterfall dialog's context using either the _begin dialog_, _prompt_, or _replace dialog_ method. + +- The prompt and begin dialog methods will push a new instance of the referenced dialog onto the top of the stack. +- The replace dialog method will pop the current dialog off the stack and push the replacing dialog onto the stack. The replaced dialog is canceled and any information that instance contained is disposed of. + +Use the _options_ parameter to pass information to the new instance of the dialog. +The options passed into the new dialog can be accessed via the step context's _options_ property in any step of the dialog. +For more information, see how to [Create advanced conversation flow using branches and loops](bot-builder-dialog-manage-complex-conversation-flow.md). + +### To continue a dialog + +Within a waterfall dialog, use the step context's _values_ property to persist state between turns. +Any value added to this collection in a previous turn is available in subsequent turns. +For more information, see how to [Create advanced conversation flow using branches and loops](bot-builder-dialog-manage-complex-conversation-flow.md). + +### To end a dialog + +Within a waterfall dialog, use the _end dialog_ method to end a dialog by popping it off the stack. The _end dialog_ method can return an optional result to the parent context (such as the dialog that called it, or the bot's turn handler). This is most often called from within the dialog to end the current instance of itself. + +You can call the end dialog method from anywhere you have a dialog context, but it will appear to the bot that it was called from the current active dialog. + +> [!TIP] +> It's best practice to explicitly call the _end dialog_ method at the end of the dialog. + +### To clear all dialogs + +If you want to pop all dialogs off the stack, you can clear the dialog stack by calling the dialog context's _cancel all dialogs_ method. + +### Repeating a dialog + +You can replace a dialog with itself, creating a loop, by using the _replace dialog_ method. +This is a great way to handle [complex interactions](~/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md) and one technique for managing menus. + +> [!NOTE] +> If you need to persist the internal state for the current dialog, you'll need to pass information to the new instance of the dialog in the call to the _replace dialog_ method, and then initialize the dialog appropriately. + +### Branch a conversation + +The dialog context maintains the dialog stack and for each dialog on the stack, tracks which step is next. Its _begin dialog_ method creates a child and pushes that dialog onto the top of the stack, and its _end dialog_ method pops the top dialog off the stack. _End dialog_ is usually called from within the dialog that's ending. + +A dialog can start a new dialog within the same dialog set by calling the dialog context's _begin dialog_ method and providing the ID of the new dialog, which then makes the new dialog the currently active dialog. The original dialog is still on the stack, but calls to the dialog context's _continue dialog_ method are only sent to the dialog that is on top of the stack, the _active dialog_. When a dialog is popped off the stack, the dialog context will resume with the next step of the waterfall on the stack where it left off of the original dialog. + +Therefore, you can create a branch within your conversation flow by including a step in one dialog that can conditionally choose a dialog to start out of a set of available dialogs. + +## Additional information + +- For more about adaptive dialogs, see the [introduction to adaptive dialogs](bot-builder-adaptive-dialog-Introduction.md). +- For information about skills, see [about skills](skills-conceptual.md). diff --git a/articles/v4sdk/bot-builder-conversations.md b/articles/v4sdk/bot-builder-conversations.md deleted file mode 100644 index ca75b87d9..000000000 --- a/articles/v4sdk/bot-builder-conversations.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Conversations within the Bot Framework SDK - Bot Service -description: Describes what a conversation is within the Bot Framework SDK. -keywords: conversation flow, recognize intent, single turn, multiple turn, bot conversation -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Conversation flow -[!INCLUDE[applies-to](../includes/applies-to.md)] - -Designing a bot's conversation flow involves deciding how a bot responds when the user says something to it. A bot first recognizes the task or conversation topic based on a message from the user. To determine the task or topic (known as the *intent*) associated with a user's message, the bot can look for words or patterns in the text of the user's message, or it can take advantage of services like [Language Understanding](bot-builder-concept-luis.md) and [QnA Maker](https://docs.microsoft.com/azure/cognitive-services/qnamaker/overview/overview). - -Once the bot has recognized the user's intent, depending on the scenario, the bot could fulfill the user's request with a single reply, completing the conversation in one turn, or it might require a series of turns. For multi-turn conversation flows, the Bot Framework SDK provides [state management](./bot-builder-howto-v4-state.md) for keeping track of a conversation, [prompts](bot-builder-prompts.md) for asking for information, and [dialogs](bot-builder-dialog-manage-conversation-flow.md) for encapsulating conversation flows. - -In a complex bot with multiple subsystems, it can be the case that you use multiple services to recognize intent, one for each subcomponent of the bot. The [dispatch tool](bot-builder-tutorial-dispatch.md) gets the results of multiple services in one place when you combine conversational subsystems into one bot. - - - -## Single turn conversation - -The simplest conversational flow is single-turn. In a single-turn flow, the bot finishes its task in one turn, consisting of one message from the user and one reply from the bot. - - - -The simplest kind of single-turn bot doesn't need to keep track of conversation state. Each time it receives a message, it responds based only on the context of the current incoming message, without knowledge of past conversational turns. - -![Single-turn weather bot](./media/concept-conversation/weather-single-turn.png) - -A weather bot might have a single-turn flow, it just gives the user a weather report, without going back and forth asking for the city or the date. All the logic for displaying the weather report is based on the message the bot just received. In each turn of a conversation, the bot receives a [turn context](bot-builder-concept-activity-processing.md#turn-context), which your bot can use to determine what to do next and how the conversation flows. - -## Multiple turns - -Most types of conversation can't be completed in a single turn, so a bot can also have a multi-turn conversation flow. Some scenarios that require multiple conversational turns include: - -* A bot that prompts the user for additional information that it needs to complete a task. The bot needs to track whether it has all the parameters for fulfilling the task. -* A bot that guides the user through steps in a process, such as placing an order. The bot needs to track where the user is in the sequence of steps. - -For example, a weather bot might have a multi-turn flow, if the bot responds to "what's the weather?" by asking for the city. - -![multi-turn weather bot](./media/concept-conversation/weather-multi-turn.png) - -When the user replies to the bot's prompt for the city and the bot receives "Seattle", the bot needs to have some context saved to understand that the current message is the response to a previous prompt and part of a request to get weather. Multi-turn bots keep track of state to respond appropriately to new messages. - -See [how to manage conversation and user state](bot-builder-howto-v4-state.md) for more information. - -> [!NOTE] -> Multi-turn conversations with REST API clients will need to keep track of their own state, for example in a database or table storage. - -## Conversation topics - -You might design your bot to handle more than one type of task. For example, you might have a bot that provides different conversation flows for greeting the user, placing an order, canceling an order, and getting help. One way to handle this switch between conversation for different tasks or conversation topics is to recognize the intent (what the user wants to do) from the current message. - -### Recognize intent - -The Bot Framework SDK supplies _recognizers_ that can process a message to determine intent, so your bot can initiate the appropriate conversational flow. Call the recognizer's _recognize_ async method to determine the user's intent from their message content. You can then call the _get top scoring intent_ method on the result to get the recognizer's top prediction. - -A recognizer could use regular expressions, language understanding, or other logic that you develop. The following are examples of possible recognizers: - -* A recognizer that uses QnA Maker to detect when a user asks a question covered in a knowledgebase. -* A recognizer that uses Language Understanding (LUIS) to train a service with examples of ways user might ask for help, and map that to the `Help` intent. -* A custom recognizer that uses regular expressions to look for commands. -* A custom recognizer that uses a service to translate input. - -### Consider how to interrupt conversation flow or change topics - -One way to keep track of where you are in a conversation is to use [conversation state](bot-builder-howto-v4-state.md) to save information about the currently active topic or what steps in a sequence have been completed. - -When a bot becomes more complex, you can also imagine a sequence of conversation flows occurring in a stack; for instance, the bot will invoke the new order flow, and then invoke the product search flow. Then the user will select a product and confirm, completing the product search flow, and then complete the order. - -However, conversations rarely follow such a linear, logical path. Users do not communicate in "stacks", instead they tend to frequently change their minds. Consider the following example: - -![User says something unexpected](./media/concept-conversation/interruption.png) - -While your bot may have logically constructed a stack of flows, the user may decide to do something entirely different or ask a question that may be unrelated to the current topic. In the example, the user asks a question rather than providing the yes/no response that the flow expects. How should your flow respond? - -* Insist that the user answer the question first. -* Disregard everything that the user had done previously, reset the whole flow stack, and start from the beginning by attempting to answer the user's question. -* Attempt to answer the user's question and then return to that yes/no question and try to resume from there. - -There is no right answer to this question, as the best solution will depend upon the specifics of your scenario and how the user would reasonably expect the bot to respond. Refer to how to [use dialogs](bot-builder-dialog-manage-conversation-flow.md) and [handle interruptions](bot-builder-howto-handle-user-interrupt.md) to manage conversation flow. - -## Conversation lifetime - - -A bot receives a _conversation update_ activity whenever it has been added to a conversation, other members have been added to or removed from a conversation, or conversation metadata has changed. -You may want to have your bot react to conversation update activities by greeting users or introducing itself. - -A bot receives an _end of conversation_ activity to indicate that the user has ended the conversation. A bot may send an _end of conversation_ activity to indicate that the conversation is ending. -If you are storing information about the conversation, you may want to clear that information when the conversation ends. - - - -Your bot can support multi-turn interactions where it prompts users for multiple pieces of information. It can be focused on a very specific task or support multiple types of tasks. -The Bot Framework SDK has some built-in support for language understanding (LUIS) and QnA Maker for adding natural language "question and answer" features to your bot. - -## Conversations, channels, and users - -Conversations can be either a _direct_ conversation with a specific user or a _group_ conversation with multiple users. -A bot communicates with a user on a channel by receiving activities from, and sending activities to the user. - -* Each user has an ID that is unique per channel. -* Each conversation has an ID that is unique per channel. -* The channel sets the conversation ID when it starts the conversation. -* The bot cannot start a conversation; however, once it has a conversation ID, it can resume that conversation. -* Not all channels support group conversations. - -## Next steps - -> [!div class="nextstepaction"] -> [Manage simple conversation flow with dialogs](bot-builder-dialog-manage-conversation-flow.md) - - - diff --git a/articles/v4sdk/bot-builder-create-a-bot-project.md b/articles/v4sdk/bot-builder-create-a-bot-project.md new file mode 100644 index 000000000..2a2960d4d --- /dev/null +++ b/articles/v4sdk/bot-builder-create-a-bot-project.md @@ -0,0 +1,253 @@ +--- +title: How bot projects are structured +description: Learn about how bot projects are structured in the Bot Framework SDK. Learn about common aspects of bot code. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Understand the structure of an echo bot + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +The Bot Framework templates and samples are written for ASP.NET (C#), restify (JavaScript), and aiohttp (Python). +However, the web service features aren't part of the Bot Framework SDK, but part of the web framework you choose to use. + +All bot applications share some common features. + +| Feature | Description | +|:-|:-| +| Resource provisioning | The bot as a web app needs to create a web service, bot adapter, and bot object. | +| Messaging endpoint | The web app needs to implement a messaging endpoint on which to receive activities and forward activities to the bot adapter. | +| Bot adapter | The adapter receives activities from the messaging endpoint, forwards them to the bot's turn handler, and catches any errors or exceptions the bot's logic doesn't catch. | +| Bot object | The bot object handles the bot's reasoning or logic for the turn. | + +You can create an echo bot from the templates, as described in [Create a bot](../bot-service-quickstart-create-bot.md), or you can copy an echo bot project from the [Microsoft/BotBuilder-Samples](https://github.com/Microsoft/BotBuilder-Samples) repository. + +The C# and JavaScript templates have built-in support for streaming connections. However, this article doesn't cover streaming features. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + +## Prerequisites + +- Knowledge of [bot basics](bot-builder-basics.md). +- A copy of the **echo bot** sample in [**C#**](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/02.echo-bot#readme), [**JavaScript**](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/02.echo-bot#readme), [**Java**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/02.echo-bot#readme), or [**Python**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/02.echo-bot#readme). + +## Bot templates + +A bot is a web application, and templates are provided for each language. + +# [C#](#tab/csharp) + +The Bot Framework includes both VSIX and .NET templates. + +The templates generate an [ASP.NET MVC Core](https://dotnet.microsoft.com/apps/aspnet/mvc) web app. If you look at the [ASP.NET](/aspnet/core/fundamentals/index?view=aspnetcore-3.1&preserve-view=true) fundamentals, you'll see similar code in files such as **Program.cs** and **Startup.cs**. These files are required for all web apps and aren't bot-specific. + +[!INCLUDE [VSIX templates](../includes/vsix-templates-versions.md)] + +The **appsettings.json** file specifies the configuration information for your bot, such as its app ID, and password among other things. If using certain technologies or using this bot in production, you'll need to add your specific keys or URL to this configuration. For this echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. + +The **EchoBot.csproj** file specifies dependencies and their associated versions for your bot. This is all set up by the template and your system. Additional dependencies can be installed using NuGet package manager or the `dotnet add package` command. + +# [JavaScript](#tab/javascript) + +The Yeoman generator creates a type of [restify](http://restify.com/) web application. If you look at the restify quickstart in their docs, you'll see an app similar to the generated **index.js** file. Some of the key files generated by the template are described in this article. Code in some files won't be copied, but you'll see it when you run the bot, and you can refer to the [Node.js echobot](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/02.echo-bot) sample. + +The **package.json** file specifies dependencies and their associated versions for your bot. This is all set up by the template and your system. Additional dependencies can be installed using `npm install`. + +The **.env** file specifies the configuration information for your bot, such as the port number, app ID, and password among other things. If using certain technologies or using this bot in production, you'll need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. + +To use the **.env** configuration file, the bot requires the `dotenv` package from npm. This is already included as a dependency in the **package.json** file. + +# [Java](#tab/java) + +The Yeoman generator creates a [Spring](https://spring.io/web-applications) based web application with a build file using [Maven](https://maven.apache.org/what-is-maven.html). + +The Maven **pom.xml** file specifies dependencies and their associated versions for your bot. This is all set up by the template and your system. Additional dependencies can be installed by adding entries to the pom.xml file. + +The **application.properties** file specifies the configuration information for your bot, such as the port number, app ID, and password among other things. If using certain technologies or using this bot in production, you'll need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. + +# [Python](#tab/python) + +The Python Cookiecutter templates create an [aiohttp](https://docs.aiohttp.org/) web application. + +The **requirements.txt** file specifies dependencies and their associated versions for your bot. This is all setup by the template and your system. Additional dependencies can be installed using `pip install -r requirements.txt` + +The **config.py** file specifies the configuration information for your bot, such as the port number, app ID, and password among other things. If using certain technologies or using this bot in production, you'll need to add your specific keys or URL to this configuration. For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time. + +--- + +## Resource provisioning + +To function as a web app, your bot needs to create a web service, bot adapter, and bot object. + +For most bots, you would also create storage layer and memory management objects for the bot. +However, the echo bot doesn't need to persist state between turns. +And for some bots, you may need to create other objects that the bot object or adapter will require. + +### [C#](#tab/csharp) + +In ASP.NET, you register objects and object creation methods in the **Startup.cs** file. +The `ConfigureServices` method loads the connected services and their keys (if there are any) from **appsettings.json**, connects state, and so on. Here, the adapter and bot are defined to be available through dependency injection. +Then, the `Configure` method finishes the configuration of your app. + +`ConfigureServices` and `Configure` are called by the runtime when the app starts. + +### [JavaScript](#tab/javascript) + +In restify, you set up the web service and the objects it needs in the **index.js** file. The service, adapter, and bot are covered separately in the following sections. + +### [Java](#tab/java) + +In Spring, you set up the web service and the objects it needs in the **application.java** file. The **application.java** has comments that denote the different components and framework classes used by the bot application. The service, adapter, and bot are covered separately in the following sections. + +### [Python](#tab/python) + +In aiohttp, you set up the web service and the objects it needs in the **app.py** file. The service, adapter, and bot are covered separately in the following sections. + +--- + +## Messaging endpoint + +The template implements a web service with a messaging endpoint. +When it receives a request, the service extracts the authentication header and request payload and forwards them to the adapter. + +The C# and JavaScript SDKs support streaming connections. While the echo bot doesn't use any of the streaming features, the adapter in the C# and JavaScript templates is designed to support them. + +Each incoming request represents the start of a new turn. + +### [C#](#tab/csharp) + +**Controllers\\BotController.cs** + +[!code-csharp[BotController](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/Controllers/BotController.cs?range=11-34&highlight=22)] + +### [JavaScript](#tab/javascript) + +**index.js** + +[!code-javascript[Create server](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/index.js?range=21-26)] + +[!code-javascript[Listen for HTTP requests](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/index.js?range=74-78&highlight=4)] + +### [Java](#tab/java) + +**Application.java** + +[!code-java[Application](~/../botbuilder-samples/samples/java_springboot/02.echo-bot/src/main/java/com/microsoft/bot/sample/echo/Application.java?range=27)] + +### [Python](#tab/python) + +**app.py** + +[!code-python[create server](~/../botbuilder-samples/samples/python/02.echo-bot/app.py?range=64-88&highlight=12)] + +--- + +## The bot adapter + +The adapter receives activities from the messaging endpoint, forwards them to the bot's turn handler, and catches any errors or exceptions the bot's logic doesn't catch. The adapter also forwards activities from your bot to the user's channel. + +The adapter allows you to add your own _on turn error_ handler. + +### [C#](#tab/csharp) + +**Startup.cs** + +The adapter to use is defined in the `ConfigureServices` method. + +[!code-csharp[adapter](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/Startup.cs?range=34-35)] + +**AdapterWithErrorHandler.cs** + +[!code-csharp[adapter](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/AdapterWithErrorHandler.cs?range=11-32)] + +### [JavaScript](#tab/javascript) + +**index.js** + +[!code-javascript[create adapter](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/index.js?range=43-69)] + +### [Java](#tab/java) + +**Application.java** + +The adapter to use is defined in the `getBotFrameworkAdapter` method. + +[!code-java[adapter](~/../botbuilder-samples/samples/java_springboot/02.echo-bot/src/main/java/com/microsoft/bot/sample/echo/Application.java?range=55-64&highlight=9)] + +The AdapterWithErrorHandler is defined in the Java SDK code, in the com.microsoft.bot.integration package. This class can be reviewed in the source code for the Java SDK. + +### [Python](#tab/python) + +**app.py** + +[!code-python[create adapter](~/../botbuilder-samples/samples/python/02.echo-bot/app.py?range=11-15,22-28,30-56,58-59)] + +--- + +## The bot logic + +The echo bot uses an _activity handler_ and implements handlers for the activity types it recognizes and reacts to, in this case, the _conversation update_ and _message_ activities. + +- A conversation update activity includes information on who has joined or left the conversation. For non-group conversations, both the bot and the user join the conversation when it starts. For group conversations, a conversation update is generated whenever someone joins or leaves the conversation, whether that's the bot or a user. +- A message activity represents a message the user sends to the bot. + +The echo bot welcomes a user when they join the conversation and echoes back any messages they send to the bot. + +### [C#](#tab/csharp) + +**Startup.cs** + +The bot to use is defined in the `ConfigureServices` method. + +[!code-csharp[EchoBot](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/Startup.cs?range=37-38)] + +**Bots\\EchoBot.cs** + +[!code-csharp[adapter](~/../botbuilder-samples/samples/csharp_dotnetcore/02.echo-bot/Bots/EchoBot.cs?range=12-31)] + +### [JavaScript](#tab/javascript) + +**index.js** + +[!code-javascript[create bot](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/index.js?range=21-22,70-72)] + +**bot.js** + +[!code-javascript[bot logic](~/../botbuilder-samples/samples/javascript_nodejs/02.echo-bot/bot.js?range=4-29)] + +### [Java](#tab/java) + +**Application.java** + +The bot to use is defined in the `getBot` method. + +[!code-java[adapter](~/../botbuilder-samples/samples/java_springboot/02.echo-bot/src/main/java/com/microsoft/bot/sample/echo/Application.java?range=40-53)] + +**EchoBot.cs** + +[!code-java[adapter](~/../botbuilder-samples/samples/java_springboot/02.echo-bot/src/main/java/com/microsoft/bot/sample/echo/EchoBot.java?range=25-47)] + +### [Python](#tab/python) + +**app.py** + +[!code-python[create bot](~/../botbuilder-samples/samples/python/02.echo-bot/app.py?range=11-15,60-61)] + +**bots/bot.py** + +[!code-python[create bot](~/../botbuilder-samples/samples/python/02.echo-bot/bots/echo_bot.py?range=4-19)] + +--- + +## Next steps + +- Learn how to [send and receive text messages](bot-builder-howto-send-messages.md) +- Learn how to [send welcome messages to users](bot-builder-send-welcome-message.md) diff --git a/articles/v4sdk/bot-builder-custom-assistant-introduction.md b/articles/v4sdk/bot-builder-custom-assistant-introduction.md deleted file mode 100644 index 33bb9753c..000000000 --- a/articles/v4sdk/bot-builder-custom-assistant-introduction.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: Custom Assistant Overview - Bot Service -description: Learn about creating your own Custom Assistant -author: darrenj -ms.author: darrenj -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/01/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Custom Assistant Overview - -## Overview - -We have seen significant need from our customers and partners to deliver a conversational assistant tailored to their brand, personalized to their customers and made available across a broad range of conversational canvases and devices. Continuing Microsoft open-sourced approach toward Bot Framework SDK, the open source Custom Personal Assistant provides full control over the end user experience built on a set of foundational capabilities. Additionally, the experience can be infused with intelligence about the end-user and any device/ecosystem information for a truly integrated and intelligent experience. - -We strongly believe our customers should own and enrich their customer relationships and insights. Therefore, any custom assistant provides complete control of the user experience to our customers and partners. The name, voice and personality can be changed to suit the organization’s needs. Our Custom Assistant solution simplifies creation of your own assistant enabling you to get started in minutes. - -The scope of Custom Personal Assistant functionality is broad, typically offering end users a range of capabilities. To increase developer productivity and to enable a vibrant ecosystem of reusable conversational experiences, we are providing developers initial examples of reusable conversational skills. These skills can be added into the conversational application to light up a specific conversation experience, such as finding a point of interest, interacting with calendar, tasks, email and many other scenarios. Skills are fully customizable and consist of language models for multiple languages, dialogs and code. - -At this time we are running an initial preview and working closely with initial customers and partners in an open-source repository to bring to life the first experiences and make it available more broadly in the coming months. - -![Custom Assistant Diagram](media/enterprise-template/CustomAssistantDiagram.jpg) - -## Complete control of the user experience - -All aspects of the end user experience are owned and controlled by you. This includes the Branding, Name, Voice, Personality, Responses and Avatar. The source-code to the Custom Assistant and supporting Skills are provided in full enabling you to adjust as required. - -## Complete ownership and control of data - -Your Custom Assistant will be deployed within your Azure subscription. Therefore all data generated by your assistant (questions asked, user behaviour, etc.) is entirely contained within your Azure subscription. See [Cognitive Services Azure Trusted Cloud](https://www.microsoft.com/trustcenter/cloudservices/cognitiveservices) and the [Azure section of the Trust Center](https://www.microsoft.com/TrustCenter/CloudServices/Azure) more specifically for more information. - -## Your Assistant, anywhere.. - -The Custom Assistant leverages the Microsoft Conversational AI platform and therefore can be surfaced through any Bot Framework [channel](https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0) – e.g. WebChat, FaceBook Messenger, Skype, etc. In addition, through the [Direct Line](https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts?view=azure-bot-service-4.0) channel we can embed experiences into Desktop and Mobile Apps including devices such as Cars, Speakers, Alarm Clocks, etc. - -## Built on Enterprise grade technology - -The Custom Assistant solution is built on the Azure Bot Service, Language Understanding Cognitive Service, Unified Speech along with a broad set of supporting Azure components meaning that you benefit from the [Azure global infrastructure](https://azure.microsoft.com/global-infrastructure/). - -In addition, Language Understanding support is provided by the LUIS Cognitive Service which supports a broad set of languages [listed here](https://docs.microsoft.com/azure/cognitive-services/luis/luis-supported-languages). The [Translator Cognitive Service](https://azure.microsoft.com/services/cognitive-services/translator-text-api/) provides additional Machine Translation capabilities to extend the reach of your Custom Assistant even further. - -## Integrated and Context Aware - -Your Custom Assistant can be integrated into your device and ecosystem enabling a truly integrated and intelligent experience . Through this contextual awareness more intelligent experiences can be developed and deliver further personalisation than otherwise possible. - -## 3rd Party assistant integration - -The Custom Assistant enables you to deliver your own unique experience but also hand-off to the end-users chosen Digital Assistant for certain types of questions. - -## Flexible integration - -Our Custom Assistant architecture is flexible and can be integrated with existing investments you may have made into device based Speech or Natural Language processing capabilities and of course integrate with your existing back-end systems and APIs. - -## Adaptive Cards - -[Adaptive Cards](https://adaptivecards.io/) provide the ability for your Custom Assistant to return User Experience elements (e.g. Cards, Images, Buttons) alongside text base responses. If the device or conversation canvas has a screen these Adaptive Cards can be rendered across a broad range of devices and platforms providing supporting User Experience where appropriate. Examples of Adaptive Cards can be found [here](https://adaptivecards.io/samples/) with information on Rendering options in the documentation [here](https://docs.microsoft.com/adaptive-cards/rendering-cards/getting-started). - - -## Skills - -In addition to the base assistant, there exists a broad set of common capabilities which require each developer to build themselves. Productivity is a great example where each organisation would need to create Language Models (LUIS), Dialogs (Code), Integration (Code) and Language Generation (Responses) to enable common scenarios such as Point of Interest, Email, Calendar or Tasks. - -This is then further complicated by the need to support multiple languages and results in a large amount of work -required for any organisation building their own assistant. - -Our Custom Assistant solution includes a new Skill capability enabling new capabilities to be plugged into an custom-assistant through configuration only. - -All aspects of each Skill (Language Model, Dialogs, Integration Code and Language Generation) are completely customisable by developers as the full source code is provided on GitHub along with the Custom Assistant. - -## Example Scenarios - -The Custom Assistant extends across a broad number of industry scenarios, example scenarios are shown below for reference purposes: - -- Automotive Industry: Voice enabled Personal Assistant integrated into the car providing end users the ability to perform traditional car operations (e.g. navigation, radio) along with productivity focused scenarios such as moving meetings when your running late, adding items to your task list and proactive experiences where the car can suggest tasks to complete based on events such as starting the engine, traveling home or enabling cruise control. Adaptive Cards are rendered within the Head Unit and Speech integration performed through Push-To-Talk or Wake Word interactions. - -- Hospitality: Voice enabled Personal Assistant integrated into a hotel-room device providing a broad range of Hospitality focused scenarios (e.g. extend your stay, request late checkout, room service) including concierge and the ability to find local restuarants and attractions. Optional linking to your Productivity accounts open up more personalised experiences such as suggested alarm calls, Weather warnings and learning of patterns across stays. An evolution of the current TV personalisation experienced in room today. - -- Enterprise: Voice and Text enabled branded Employee Assistant experiences integrated into enterprise devices and existing conversation canvases (e.g. Teams, WebChat, Slack) enabling employees to manage their calendars, find available meeting rooms, find people with specific skills or perform HR related operations. - -## Getting Started - -At this time we are running an initial preview and working closely with initial customers and partners in an open-source repository to bring to life the first experiences and make it available more broadly in the coming months. To register your interest and get started please fill in [this form](https://aka.ms/customassistantpreviewform) and we'll be in touch. - diff --git a/articles/v4sdk/bot-builder-custom-storage.md b/articles/v4sdk/bot-builder-custom-storage.md index 232538ab7..f027ceb97 100644 --- a/articles/v4sdk/bot-builder-custom-storage.md +++ b/articles/v4sdk/bot-builder-custom-storage.md @@ -1,197 +1,254 @@ --- -title: Implement custom storage for your bot - Bot Service -description: How to build custom storage in Bot Framework SDK v4.0 +title: Implement custom storage for your bot +description: Learn how to use version 4.0 of the Bot Framework SDK to store bot state data. Understand the default framework. See how to expand support. keywords: custom, storage, state, dialog -author: johnataylor -ms.author: johtaylo -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Implement custom storage for your bot -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -A bot’s interactions fall into three areas: firstly, the exchange of Activities with the Azure Bot Service, secondly, -the loading and saving of dialog state with a Store and finally any other backend services the bot needs to work with -to get its job done. +A bot's interactions fall into three areas: the exchange of activities with Azure AI Bot Service, the loading and saving of bot and dialog state with a memory store, and integration with back-end services. -![scaleout diagram](../media/scale-out/scale-out-interaction.png) +:::image type="content" source="../media/scale-out/scale-out-interaction.png" alt-text="Interaction diagram outlining relationship between the Azure AI Bot Service, a bot, a memory store, and other services."::: +This article explores how to extend the semantics between the Azure AI Bot Service and the bot's memory state and storage. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites -- The full sample code used in this article can be found here: [C# sample](https://aka.ms/scale-out). -In this article, we will be exploring the semantics around the bot’s interactions with the Azure Bot Service and the Store. +- Knowledge of [Basics of the Microsoft Bot Framework](bot-builder-basics.md), [Event-driven conversations using an activity handler](bot-activity-handler-concept.md), and [Managing state](bot-builder-concept-state.md). +- A copy of the scale-out sample in [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/42.scaleout), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/42.scaleout), or [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/42.scaleout). + +This article focuses on the C# version of the sample. + +## Background + +The Bot Framework SDK includes a default implementation of bot state and memory storage. +This implementation fits the needs of applications where the pieces are used together with a few lines of initialization code, as demonstrated in many of the samples. -The Bot Framework includes a default implementation; this implementation will most likely fit the needs of many -applications, and all that is needed to be done to make use of it is to plug the pieces together with a few lines of -initialization code. Many of the samples illustrate just that. +The SDK is a framework and not an application with fixed behavior. +In other words, the implementation of many of the mechanisms in the framework is a default implementation and not the only possible implementation. +The framework doesn't dictate the relationship between the exchange of activities with Azure AI Bot Service and the loading and saving of any bot state. -The goal here, however, is to describe what you can do when the semantics of the default implementation doesn’t quite work -as you might like in your application. The basic point is that this is a framework and not a canned application with -fixed behavior, in other words, the implementation of many of the mechanisms in the framework is just the default -implementation and not the only implementation. +This article describes one way to modify the semantics of the default state and storage implementation when it doesn't quite work for your application. +The scale-out sample provides an alternate implementation of state and storage that has different semantics than the default ones. +This alternate solution sits equally well in the framework. +Depending on your scenario, this alternate solution may be more appropriate for the application you're developing. -Specifically, the framework does not dictate the relationship between the exchange of Activities with the Azure -Bot Service and the loading and saving of any Bot state; it simply provides a default. To illustrate this point further, -we will be developing an alternative implementation that has different semantics. This alternative solution sits -equally well in the framework and may even be more appropriate for the application being developed. It all depends on the scenario. +## Behavior of the default adapter and storage provider -## Behavior of the default BotFrameworkAdapter and Storage providers +With the default implementation, on receiving an activity, the bot loads the state corresponding to the conversation. +It then runs the dialog logic with this state and the inbound activity. +In the process of running the dialog, one or more outbound activities are created and immediately sent. +When the processing of the dialog is complete, the bot saves the updated state, overwriting the old state. -Firstly, let’s review the default implementation that ships as part of the framework packages as shown by the following -sequence diagram: +:::image type="content" source="../media/scale-out/scale-out-default.png" alt-text="Sequence diagram showing the default behavior of a bot and its memory store."::: -![scaleout diagram](../media/scale-out/scale-out-default.png) +However, a few things can go wrong with this behavior. -On receiving an Activity, the bot loads the state corresponding to this conversation. It then runs the dialog logic -with this state and the Activity that has just arrived. In the process of executing the dialog, one or more outbound -activities are created and immediately sent. When the processing of the dialog is complete, the bot saves the updated -state, overwriting the old state with new. +- If the save operation fails for some reason, then state has implicitly slipped out of sync with what the user sees on the channel. + The user has seen responses from the bot and believes that the state has moved forward, but it hasn't. + This error can be worse than if the state update succeeded but the user didn't receive the response messages. -It is worth considering a couple of things that can go wrong with this behavior. + Such state errors can have implications for your conversation design. + For example, the dialog might require extra, otherwise redundant, confirmation exchanges with the user. -Firstly, if the Save operation were to fail for some reason the state has implicitly slipped out of sync with -what is seen on the channel because the user having seen the responses is under the impression that the state -has moved forward, but it hasn’t. This is generally worse than if the state was successful and the response -messaging were successful. This can have implications for the conversation design: for example, the dialog might -include additional, otherwise redundant confirmation exchanges with the user. +- If the implementation is deployed _scaled out_ across multiple nodes, the state can accidentally get overwritten. + This error can be confusing because the dialog will likely have sent activities to the channel carrying confirmation messages. -Secondly, if the implementation is deployed scaled out across multiple nodes, the state can accidentally get -overwritten - this can be particularly confusing because the dialog will likely have sent activities to the channel -carrying confirmation messages. Consider the example of a pizza order bot, if the user, on being asked for a topping, -adds mushroom and without delay adds cheese, in a scaled-out scenario with multiple instances running subsequent -activities can be sent concurrently to different machines running the bot. When this happens, there is what is referred -to as a “race condition” where one machine might overwrite the state written by another. However, in our scenario, -because the responses were already sent, the user has received confirmation that both mushroom and cheese were added. -Unfortunately, when the pizza arrives, it will only contain mushroom or cheese, not both. + Consider a pizza order bot, where the bot asks user for topping choices, and the user sends two rapid messages: one to add mushrooms and one to add cheese. + In a scaled-out scenario, multiple instances of the bot might be active, and the two user messages could be handled by two separate instances on separate machines. + Such a conflict is referred to as a _race condition_, where one machine might overwrite the state written by another. + However, because the responses were already sent, the user received confirmation that both mushroom and cheese were added to their order. + Unfortunately, when the pizza arrives, it only contains mushroom or cheese, but not both. ## Optimistic locking -The solution is to introduce some locking around the state. The particular style of locking we will be using here is called -optimistic locking because we will let everything run as if they were each the only thing running and then we will detect any -concurrency violations after the processing has been done. This may sound complicated but is very easy to build using cloud -storage technologies and the right extension points in the bot framework. - -We will use a standard HTTP mechanism based on the entity tag header, (ETag). Understanding this mechanism is crucial to -understanding the code that follows. The following diagram illustrates the sequence. - -![scaleout diagram](../media/scale-out/scale-out-precondition-failed.png) - -The diagram illustrates the case of two clients that are performing an update to some resource. When a client issues a -GET request and a resource is returned from the server, it is accompanied by an ETag header. The ETag header is an opaque -value that represents the state of the resource. If a resource is changed, the ETag will be updated. When the client has -done its update to the state, it POSTs it back to the server, making this request the client attaches the ETag value it had -previously received in a precondition If-Match header. If this ETag does not match the value, the server last returned -(on any response, to any client) the precondition check fails with a 412 Precondition Failure. This failure is an indicator -to the client making the POST request that the resource has been updated. On seeing this failure, the typical behavior for -a client will be to GET the resource again, apply the update it wanted, and POST the resource back. This second -POST will be successful, assuming of course, that no other client has come and updated the resource, and if it has the -client will just have to try again. - -This process is called “optimistic” because the client, having got hold of a resource proceeds to do its processing, -the resource itself is not “locked” in the sense that other clients can access it without any restriction. Any contention -between clients over what the state of the resource should be is not determined until the processing has been done. As a -rule, in a distributed system this strategy is more optimal than the opposite “pessimistic” approach. - -The optimistic locking mechanism we’ve covered assumes program logic can be safely retried, needless, to say the important -thing to consider here is what happens to external service calls. The ideal solution here is if these services can be made -idempotent. In computer science, an idempotent operation is one that has no additional effect if it is called more than once -with the same input parameters. Pure HTTP REST services that implement GET, PUT and DELETE fit this description. The reasoning -here is intuitive: we might be retrying the processing and so making any calls it needs to make have no additional effect as -they are re-executed as part of that retry is a good thing. For the sake of this discussion, we will assume we are living in -an ideal world and the backend services shown to the right of the system picture at the beginning of this article are all -idempotent HTTP REST services, from here on we will focus only on the exchange of activities. +The scale-out sample introduces some locking around the state. +The sample implements _optimistic locking_, which lets each instance run as if it were the only one running and then check for any concurrency violations. +This locking may sound complicated, but known solutions exist, and you can use cloud storage technologies and the right extension points in the Bot Framework. + +The sample uses a standard HTTP mechanism based on the entity tag header (ETag). +Understanding this mechanism is crucial to understanding the code that follows. The following diagram illustrates the sequence. + +:::image type="content" source="../media/scale-out/scale-out-precondition-failed.png" alt-text="Sequence diagram showing a race condition, with the second update failing."::: + +The diagram has two clients that are performing an update to some resource. + +1. When a client issues a GET request and a resource is returned from the server, the server includes an ETag header. + + The ETag header is an opaque value that represents the state of the resource. + If a resource is changed, the server updates its ETag for the resource. + +1. When the client wants to persist a state change, it issues a POST request to the server, with the ETag value in an `If-Match` precondition header. +1. If the request's ETag value doesn't match the server's, then the precondition check fails with a `412` (Precondition Failed) response. + + This failure indicates that the current value on server no longer matches the original value the client was operating on. + +1. If the client receives a precondition failed response, the client typically gets a fresh value for the resource, applies the update it wanted, and attempts to post the resource update again. + + This second POST request succeeds if no other client has updated the resource. Otherwise, the client can try again. + +This process is called _optimistic_ because the client, once it has a resource, proceeds to do its processing—the resource itself isn't _locked_, as other clients can access it without any restriction. +Any contention between clients over what the state of the resource should be isn't determined until the processing has been done. +In a distributed system, this strategy is often more optimal than the opposite _pessimistic_ approach. + +The optimistic locking mechanism as described assumes that your program logic can be safely retried. +The ideal situation is where these service requests are _idempotent_. +In computer science, an idempotent operation is one that has no extra effect if it's called more than once with the same input parameters. +Pure HTTP REST services that implement the GET, PUT, and DELETE requests are often idempotent. +If a service request won't produce extra effects, then requests can be safely re-executed as part of a retry strategy. + +The scale-out sample and the remainder of this article assume that the backend services your bot uses are all idempotent HTTP REST services. ## Buffering outbound activities -The sending of an Activity is not an idempotent operation, nor is it clear that would make much sense in the end-to-end scenario. -After all the Activity is often just carrying a message that is appended to a view or perhaps spoken by a text to speech agent. +Sending an activity isn't an idempotent operation. +The activity is often a message that relays information to the user, and repeating the same message two or more times might be confusing or misleading. + +Optimistic locking implies that your bot logic may need to be rerun multiple times. +To avoid sending any given activity multiple times, wait for the state update operation to succeed before sending activities to the user. +Your bot logic should look something like the following diagram. + +:::image type="content" source="../media/scale-out/scale-out-buffer.png" alt-text="Sequence diagram with messages being sent after dialog state is saved."::: -The key thing we want to avoid with sending the activities is sending them multiple times. The problem we have is that the optimistic locking mechanism requires that we with rerun our logic possibly multiple times. The solution is simple: we must -buffer the outbound activities from the dialog until we are sure we are not going to rerun the logic. That is until after we -have a successful Save operation. We are looking for a flow that looks something like the following: +Once you build a retry loop into your dialog execution, you have the following behavior when there's a precondition failure on the save operation. -![scaleout diagram](../media/scale-out/scale-out-buffer.png) +:::image type="content" source="../media/scale-out/scale-out-save.png" alt-text="Sequence diagram with messages being sent after a retry attempt succeeds."::: -Assuming we can build a retry loop around the dialog execution we get the following behavior when there is a -precondition failure on the Save operation: +With this mechanism in place, the pizza bot from the earlier example should never send an erroneous positive acknowledgment of a pizza topping being added to an order. +Even with the bot deployed across multiple machines, the optimistic locking scheme effectively serializes the state updates. +In the pizza bot, the acknowledgment from adding an item can now even reflect the full state accurately. +For example, if the user quickly types "cheese" and then "mushroom", and these messages are handled by two different instances of the bot, the last instance to complete can include "a pizza with cheese and mushroom" as part of its response. -![scaleout diagram](../media/scale-out/scale-out-save.png) +This new custom storage solution does three things that the default implementation in the SDK doesn't do: -Applying this mechanism and revisiting our example from earlier we should never see an erroneous positive acknowledgment of a pizza topping being added to an order. In fact, although we might have scaled out our deployment across multiple machines, we have effectively serialized our state updates with the optimistic locking scheme. In our pizza ordering but the acknowledgement from adding an item can now even be written to reflect the full state accurately. For example, if the user immediately types “cheese” and then before the bot has had a chance to reply “mushroom” the two replies can now be “pizza with cheese” and then “pizza with cheese and mushroom.” +1. It uses ETags to detect contention. +1. It retries the processing when an ETag failure is detected. +1. It waits to send outbound activities until it has successfully saved state. -Looking at the sequence diagram we can see that the replies could be lost after a successful Save operation, however, they could be lost anywhere in the end to end communication. The point is this is not a problem the state management infrastructure can fix. It will require a higher-level protocol and possibly one involving the user of the channel. For example, if the bot appears to the user not to have replied it is reasonable to expect the user to ultimately try again or some such behavior. So while it is reasonable for a scenario to have occasional transient outages such as this it is far less reasonable to expect a user to be able to filter out erroneous positive acknowledgements or other unintended messages. +The remainder of this article describes the implementation of these three parts. -Pulling this all together, in our new custom storage solution, we are going to do three things the default implementation in the framework doesn’t do. Firstly, we are going to use ETags to detect contention, secondly we are going to retry the processing when the ETag failure is detected and thirdly we are going to buffer any outbound Activities until we have a successful save. The remainder of this article describes the implementation of these three parts. +## Implement ETag support -## Implementing ETag Support +First, define an interface for our new store that includes ETag support. +The interface helps use the dependency injection mechanisms in ASP.NET. +Starting with the interface allows you to implement separate versions for unit tests and for production. +For example, the unit test version might run in memory and not require a network connection. -To support unit testing we start out by defining an interface for our new store with ETag support. Having the interface means we can write two versions, one for the unit tests that runs in memory without the need of hitting the network and another for production. The interface will make it very easy to leverage the dependency injection mechanisms we have in ASP.NET. +The interface consists of _load_ and _save_ methods. +Both methods will use a _key_ parameter to identify the state to load from or save to storage. -The interface consists of Load and Save methods. Both these take the key we will use for the state. The Load will return the data and the associated ETag. And the Save will take these in. Additionally, the Save will return bool. This bool will indicate whether the ETag has matched and the Save was successful. This is not intended as a general error indicator but rather a specific indicator of precondition failure, we model this as a return code rather than an exception because we will be writing control flow logic around this in the shape of our retry loop. +- _Load_ will return the state value and associated ETag. +- _Save_ will have parameters for the state value and associated ETag and return a Boolean value that indicates whether the operation succeeded. + The return value won't serve as a general error indicator, but instead as a specific indicator of precondition failure. + Checking the return code will part of the logic of the retry loop. -As we would like this lowest level storage piece to be pluggable, we will make sure to avoid placing any serialization requirements on it, however we would like to specify that the content save should be JSON, that way a store implementation can set the content-type. The easiest and most natural way to do this in .NET is through the argument types, specifically we will type the content argument as JObject. In JavaScript or TypeScript this will just be a regular native object. +To make the storage implementation widely applicable, avoid placing serialization requirements on it. +However, many modern storage services support JSON as the content-type. +In C#, you can use the `JObject` type to represent a JSON object. +In JavaScript or TypeScript, JSON is a regular native object. -This is the resulting interface: +Here's a definition of the custom interface. + +**IStore.cs** -**IStore.cs** [!code-csharp[IStore](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/IStore.cs?range=14-19)] -Implementing this against Azure Blob Storage is straight forward. +Here's an implementation for Azure Blob Storage. + +**BlobStore.cs** -**BlobStore.cs** [!code-csharp[BlobStore](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/BlobStore.cs?range=18-101)] -As you can see Azure Blob Storage is doing the real work here. Note the catch of specific exceptions and how that is translated across to meet what will be the expectations of the calling code. That is, on the load we want a Not Found exception to return null and the Precondition Failed exception on the Save to return bool. +Azure Blob Storage does much of the work. Each method checks for a specific exception to meet the expectations of the calling code. + +- The `LoadAsync` method, in response to a storage exception with a _not found_ status code, returns a null value. +- The `SaveAsync` method, in response to a storage exception with a _precondition failed_ code, returns `false`. -All this source code will be available in a corresponding [sample](https://aka.ms/scale-out) and that sample will include a memory store implementation. +## Implement a retry loop -## Implementing the Retry Loop -The basic shape of the loop is derived directly from the behavior shown in the sequence diagrams. +The design of the retry loop implements the behavior shown in the sequence diagrams. -On receiving an Activity we create a key for the corresponding state for that conversation. We are not changing the relationship between Activity and conversation state, so we will be creating the key in exactly the same way as in the default state implementation. +1. On receiving an activity, create a key for the conversation state. -After having created the appropriate key we will attempt to Load the corresponding state. Then run the bot’s dialogs and then attempt to Save. If that Save is successful, we will send the outbound Activities that resulted from running the dialog and be done. Otherwise we will go back and repeat the whole process from before the Load. Redoing the Load will give us a new ETag and so next time the Save will hopefully be successful. + The relationship between an activity and the conversation state is the same for the custom storage as for the default implementation. + Therefore, you can construct the key the same way that the default state implementation does. -The resulting OnTurn implementation looks like this: +1. Attempt to load the conversation state. +1. Run the bot's dialogs and capture the outbound activities to send. +1. Attempt to save the conversation state. + - On success, send the outbound activities and exit. + - On failure, repeat this process from the step to load the conversation state. + + The new load of conversation state gets a new and current ETag and conversation state. The dialog is rerun, and the save state step has a chance to succeed. + +Here's an implementation for the message activity handler. + +**ScaleoutBot.cs** -**ScaleoutBot.cs** [!code-csharp[OnMessageActivity](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/Bots/ScaleOutBot.cs?range=43-72)] -Note that we have modeled the dialog execution as a function call. Perhaps a more sophisticated implementation would have defined an interface and made this dependency injectable but for our purposes having the dialog all sit behind a static function emphasize the functional nature of our approach. As a general statement, organizing our implementation such that the crucial parts become functional puts us in a very good place when it comes to having it work successfully on networks. +> [!NOTE] +> The sample implements dialog execution as a function call. +> A more sophisticated approach might be to define an interface and use dependency injection. +> For this example, however, the static function emphasizes the functional nature of this optimistic locking approach. +> In general, when you implement the crucial parts of your code in a functional way, you improve its chances to work successfully on networks. + +## Implement an outbound activity buffer +The next requirement is to buffer outbound activities until after a successful save operation happens, which requires a custom adapter implementation. +The custom `SendActivitiesAsync` method shouldn't send the activities to the use, but add the activities to a list. +Your dialog code won't need modification. -## Implementing outbound Activity buffering +- In this particular scenario, the _update activity_ and _delete activity_ operations aren't supported and the associated methods will throw _not implemented_ exceptions. +- The return value from the send activities operation is used by some channels to allow a bot to modify or delete a previously sent message, for example, to disable buttons on cards displayed in the channel. These message exchanges can get complicated, particularly when state is required, and are outside the scope of this article. +- Your dialog creates and uses this custom adapter, so it can buffer activities. +- Your bot's turn handler will use a more standard `AdapterWithErrorHandler` to send the activities to the user. -The next requirement is that we buffer outbound Activities until a successful Save has been performed. This will require a custom BotAdapter implementation. In this code, we will implement the abstract SendActivity function to add the Activity to a list rather than sending it. The dialog we will be hosting will be non-the-wiser. -In this particular scenario UpdateActivity and DeleteActivity operations are not supported and so will just throw Not Implemented from those methods. We also don’t care about the return value from the SendActivity. This is used by some channels in scenarios where updates to Activities need to be sent, for example, to disable buttons on cards displayed in the channel. These message exchanges can get complicated particularly when state is required, that is outside the scope of this article. The full implementation of the custom BotAdapter looks like this: +Here's an implementation of the custom adapter. + +**DialogHostAdapter.cs** -**DialogHostAdapter.cs** [!code-csharp[DialogHostAdapter](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/DialogHostAdapter.cs?range=19-46)] -## Integration +## Use your custom storage in a bot + +The last step is to use these custom classes and methods with existing framework classes and methods. -All that is left to do is glue these various new pieces together and plug them into the existing framework pieces. The main retry loop just sits in the IBot OnTurn function. It holds our custom IStore implementation which for testing purposes we have made dependency injectable. We have put all the dialog hosting code into a class called DialogHost that exposes a single public static function. This function is defined to take the inbound Activity and the old state and then return the resulting Activities and new state. +- The main retry loop becomes part of your bot's `ActivityHandler.OnMessageActivityAsync` method and includes your custom storage through dependency injection. +- The dialog hosting code is added to `DialogHost` class that exposes a static `RunAsync` method. The dialog host: + - Takes the inbound activity and the old state and then returns the resulting activities and new state. + - Creates the custom adapter and otherwise runs the dialog in the same way as the SDK does. + - Creates a custom state property accessor, a shim that passes the dialog state into the dialog system. + The accessor uses reference semantics to pass an accessor handle to the dialog system. -The first thing to do in this function is to create the custom BotAdapter we introduced earlier. Then we will just run the dialog in exactly the same was as we usually do by creating a DialogSet and DialogContext and doing the usual Continue or Begin flow. The only piece we haven’t covered is the need for a custom Accessor. This turns out to be a very simple shim that facilitates passing the dialog state into the dialog system. The Accessor uses ref semantics when working with the dialog system and so all that is needed is to pass the handle across. To make things even clearer we have constrained the class template we are using to ref semantics. +> [!TIP] +> The JSON serialization is added inline to the hosting code to keep it outside of the pluggable storage layer, so that different implementations can serialize differently. -We are being very cautious in the layering, we are putting the JsonSerialization inline in our hosting code because we didn’t want it inside the pluggable storage layer when different implementations might serialize differently. +Here's an implementation of the dialog host. -Here is the driver code: +**DialogHost.cs** -**DialogHost.cs** [!code-csharp[DialogHost](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/DialogHost.cs?range=22-72)] -And finally, the custom Accessor, we only need to implement Get because the state is by ref: +And finally, here's an implementation of the custom state property accessor. + +**RefAccessor.cs** -**RefAccessor.cs** [!code-csharp[RefAccessor](~/../botbuilder-samples/samples/csharp_dotnetcore/42.scaleout/RefAccessor.cs?range=22-60)] ## Additional information -The [C# sample](https://aka.ms/scale-out) code used in this article is available on GitHub. +The scale-out sample is available from the Bot Framework samples repo on GitHub in [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/42.scaleout), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/42.scaleout), and [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/42.scaleout). diff --git a/articles/v4sdk/bot-builder-debug-transcript.md b/articles/v4sdk/bot-builder-debug-transcript.md index b8535dd7d..d5b283c3f 100644 --- a/articles/v4sdk/bot-builder-debug-transcript.md +++ b/articles/v4sdk/bot-builder-debug-transcript.md @@ -1,72 +1,92 @@ --- -title: Debug your bot using transcript files - Bot Service -description: Understand how use a transcript file to help debug your bot. +title: Debug your bot using transcript files +description: Learn how to use transcript files to debug bots. See how to create and retrieve these files, which provide detailed sets of user interactions and bot responses. keywords: debugging, faq, transcript file, emulator -author: DanDev33 -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.subservices: sdk -ms.date: 05/23/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Debug your bot using transcript files -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] One of the keys to successful testing and debugging a bot is your ability to record and examine the set of conditions that occur when running your bot. This article discusses the creation and use of a bot transcript file to provide a detailed set of user interactions and bot responses for testing and debugging. ## The bot transcript file -A bot transcript file is a specialized JSON file that preserves the interactions between a user and your bot. A transcript file preserves not only the contents of a message, but also interaction details such as the user id, channel id, channel type, channel capabilities, time of the interaction, etc. All of this information can then be used to help find and resolve issues when testing or debugging your bot. + +A bot transcript file is a specialized JSON file that preserves the interactions between a user and your bot. A transcript file preserves not only the contents of a message, but also interaction details such as the user ID, channel ID, channel type, channel capabilities, time of the interaction, and so on. All of this information can then be used to help find and resolve issues when testing or debugging your bot. ## Creating/Storing a bot transcript file -This article shows how to create bot transcript files using Microsoft's [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator). Transcript files may also be created programatically; you can read more concerning that approach [here](./bot-builder-howto-v4-storage.md#blob-transcript-storage). In this article we will use the Bot Framework sample code for [Multi Turn Prompt Bot](https://aka.ms/cs-multi-prompts-sample) that requests a user's mode of transportation, name and age, but any code that can be accessed using Microsoft's Bot Framework Emulator may be used to create a transcript file. -To begin this process ensure that the bot code you want to test is running within your development environment. Start the bot framework emulator, select the _Open Bot_ button, then enter the address of _localhost:port_ shown in your browser followed by "/api/messages" as shown in the image below. Now click the _Connect_ button to connect the emulator to your bot. +This article shows how to create bot transcript files using the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator). Transcript files may also be created programmatically; see [Blob transcript storage](./bot-builder-howto-v4-storage.md#blob-transcript-storage) to read more concerning that approach. In this article, we'll use the Bot Framework sample code for [Multi Turn Prompt Bot](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt) that requests a user's mode of transportation, name and age, but any code that can be accessed using Microsoft's Bot Framework Emulator may be used to create a transcript file. + +To begin this process, ensure that the bot code you want to test is running within your development environment. + +1. Start the Emulator. +1. On the **Welcome** tab, select **Open Bot**. +1. Enter the address of the port to which your bot is listening, followed by `/api/messages`, for instance, `http://localhost:3978/api/messages`. + + If your bot is configured with a Microsoft app ID and password, enter the ID and password in the **Open a bot** dialog. Otherwise, the Emulator won't be able to connect to your bot. + +1. Select **Connect** to connect the Emulator to your bot. -![connect emulator to your code](./media/emulator_open_bot_configuration.png) + :::image type="content" source="./media/emulator_open_bot_configuration.png" alt-text="Screenshot of dialog for connecting to a bot from the Emulator."::: -After connecting the emulator to your running code, test your code by sending simulated user interactions to the bot. For this example we have passed in the user's mode of transportation, name and age. After you have entered all of the user interactions you want to preserve, use the bot framework emulator to create and save a transcript file containing this conversation. +Test your code by interacting with your bot in the Emulator. After you've entered all of the user interactions you want to preserve, use the Bot Framework Emulator to create and save a transcript file containing this conversation. -Within the _Live Chat_ tab (shown below), select the _Save transcript_ button. +1. In the **Live Chat** tab, select **Save transcript**. -![select save transcript](./media/emulator_transcript_save.png) + :::image type="content" source="./media/emulator_transcript_save.png" alt-text="Screenshot of a conversation and the 'save transcript' button in the Emulator."::: -Choose a location and name for your transcript file and then select the save button. +1. Choose a location and name for your transcript file and select **Save**. -![transcript saveas ursula](./media/emulator_transcript_saveas_ursula.png) + :::image type="content" source="./media/emulator_transcript_saveas_ursula.png" alt-text="Screenshot of the 'save conversation transcript' dialog."::: -All of the user interactions and bot responses that you entered to test your code with the emulator have now been saved into a transcript file that you can later reload to help debug interactions between your user and your bot. +All of the user interactions and bot responses that you entered to test your code with the Emulator have now been saved into a transcript file that you can later reload to help debug interactions between your user and your bot. ## Retrieving a bot transcript file -To retrieve a bot transcript file using the Bot Framework Emulator, select the _File_ then _Open Transcript..._ in the upper left corner of the emulator, as shown below. Next, select the transcript file that you want to retrieve. (Transcripts may also be accessed from within the _TRANSCRIPTS_ list control in the _RESOURCES_ section of the emulator) -In this example we are retrieving the transcript file named "ursula_user.transcript". Selecting a transcript file will automatically load the entire preserved converation into a new Tab titled _Transcript_. +When you open a transcript file, the Emulator loads the saved conversation into a new tab. -![retrieve saved transcript](./media/emulator_transcript_retrieve.png) +To retrieve a bot transcript file: + +1. Open the Emulator. +1. From the menu, select **File** then **Open Transcript**. +1. Use the **Open transcript file** to select and open the transcript file you want to retrieve. + +:::image type="content" source="./media/emulator_transcript_retrieve.png" alt-text="Screenshot of the 'open transcript file' dialog."::: ## Debug using transcript file -With your transcript file loaded, you are now ready to debug interactions that you captured between a user and your bot. To do this, simply click on any event or activity recorded in the _LOG_ section shown in the lower right area of the emulator. In the example shown below, we selected the user's first interaction when they sent the message "Hello". When we do this, all of the information in your transcript file concerning this specific interaction is displayed in the emulator's _INSPECTOR_ window in JSON format. Looking at some of these values from the bottom upward, we see the: -* Interaction type was _message_. -* Time the message was sent. -* Plain text sent contained "Yes". -* Message was sent to our bot. -* User id and information. -* Channel id, capabilities and information. -![debug using transcript](./media/emulator_transcript_debug.png) +With your transcript file loaded, you're now ready to debug interactions that you captured between a user and your bot. + +1. Select any user or bot message, or activity recorded in the Emulator's _log_ pane. +1. The Emulator will display the activity information in the _inspector_ pane. The activity information is the payload of the HTTP request for the activity. + + A message activity includes: + + - The activity type + - The time the activity was sent from or received by the channel + - Information about the user's channel + - Information about the sender and receiver of the activity, in the `from` and `recipient` fields, respectively + - Information specific to the type of activity, such as the message text for a message activity. -This detailed level of information allows you to follow the step-by-step interactions between the user's input and your bot's response, which is useful for debugging situations where your bot either did not respond back in the manner that you anticipated or did not respond back to the user at all. Having both these values and a record of the steps leading up to the failed interaction allows you to step through your code, find the location where your bot does not respond as anticipated, and resolve those issues. +This detailed level of information allows you to follow the step-by-step interactions between the user's input and your bot's response, which is useful for debugging situations where your bot either didn't respond in the manner that you anticipated or didn't respond to the user at all. Having both these values and a record of the steps leading up to the failed interaction allows you to step through your code, find the location where your bot doesn't respond as anticipated, and resolve those issues. -Using transcript files together with the Bot Framework Emulator is just one of the many tools you can use to help you test and debug your bot's code and user interactions. To find more ways to test and debug your bot, see the additional resources listed below. +Using transcript files together with the Bot Framework Emulator is just one of the many tools you can use to help you test and debug your bot's code and user interactions. ## Additional information -For additional testing and debugging information see: +For more testing and debugging information, see: -* [Bot testing and debugging guidelines](./bot-builder-testing-debugging.md) -* [Debug with the bot framework emulator](../bot-service-debug-emulator.md) -* [Troubleshoot general problems](../bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. -* [Debugging in Visual Studio](https://docs.microsoft.com/visualstudio/debugger/index) +- [Bot testing and debugging guidelines](./bot-builder-testing-debugging.md) +- [Debug with the Bot Framework Emulator](../bot-service-debug-emulator.md) +- [Troubleshoot general problems](../bot-service-troubleshoot-bot-configuration.md) and the other troubleshooting articles in that section. +- [Debugging in Visual Studio](/visualstudio/debugger/index) diff --git a/articles/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md b/articles/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md index e9a65e399..e01dc06c4 100644 --- a/articles/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md +++ b/articles/v4sdk/bot-builder-dialog-manage-complex-conversation-flow.md @@ -1,32 +1,36 @@ --- -title: Create advanced conversation flow using branches and loops - Bot Service +title: Create advanced conversation flow using branches and loops description: Learn how to manage a complex conversation flow with dialogs in the Bot Framework SDK. keywords: complex conversation flow, repeat, loop, menu, dialogs, prompts, waterfalls, dialog set author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/30/2020 +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Create advanced conversation flow using branches and loops -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] You can create complex conversation flows using the dialogs library. This article covers how to manage complex conversations that branch and loop and how to pass arguments between different parts of the dialog. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + ## Prerequisites - Knowledge of [bot basics][concept-basics], [managing state][concept-state], the [dialogs library][concept-dialogs], and how to [implement sequential conversation flow][simple-dialog]. -- A copy of the complex dialog sample in [**C#**][cs-sample], [**JavaScript**][js-sample], or [**Python**][python-sample]. +- A copy of the complex dialog sample in [**C#**][cs-sample], [**JavaScript**][js-sample], [**Java**][java-sample], or [**Python**][python-sample]. ## About this sample This sample represents a bot that can sign users up to review up to two companies from a list. -The bot uses 3 component dialogs to manage the conversation flow. +The bot uses three component dialogs to manage the conversation flow. Each component dialog includes a waterfall dialog and any prompts needed to gather user input. These dialogs are described in more detail in the following sections. It uses conversation state to manage its dialogs and uses user state to save information about the user and which companies they want to review. @@ -37,19 +41,23 @@ The bot derives from the activity handler. Like many of the sample bots, it welc To use dialogs, install the **Microsoft.Bot.Builder.Dialogs** NuGet package. -![Complex bot flow](./media/complex-conversation-flow.png) +:::image type="content" source="./media/complex-conversation-flow.png" alt-text="Class diagram for C# sample."::: ### [JavaScript](#tab/javascript) To use dialogs, your project needs to install the **botbuilder-dialogs** npm package. -![Complex bot flow](./media/complex-conversation-flow-js.png) +:::image type="content" source="./media/complex-conversation-flow-js.png" alt-text="Class diagram for JavaScript sample."::: + +### [Java](#tab/java) + +:::image type="content" source="./media/complex-conversation-flow-java.png" alt-text="Class diagram for Java sample."::: ### [Python](#tab/python) To use dialogs, your project needs to install the **botbuilder-dialogs** PyPI package by running `pip install botbuilder-dialogs`. -![Complex bot flow](./media/complex-conversation-flow-python.png) +:::image type="content" source="./media/complex-conversation-flow-python.png" alt-text="Class diagram for Python sample."::: --- @@ -69,6 +77,12 @@ The user profile will contain information gathered by the dialogs, the user's na [!code-javascript[UserProfile class](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/userProfile.js?range=4-12)] +### [Java](#tab/java) + +**UserProfile.java** + +[!code-java[UserProfile class](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/UserProfile.java?range=9-66)] + ### [Python](#tab/python) **data_models/user_profile.py** @@ -79,7 +93,7 @@ The user profile will contain information gathered by the dialogs, the user's na ## Create the dialogs -This bot contains 3 dialogs: +This bot contains three dialogs: - The main dialog starts the overall process and then summarizes the collected information. - The top-level dialog collects the user information and includes branching logic, based on the user's age. @@ -87,7 +101,7 @@ This bot contains 3 dialogs: ### The main dialog -The main dialog has 2 steps: +The main dialog has two steps: 1. Start the top-level dialog. 1. Retrieve and summarize the user profile that the top-level dialog collected, save that information to user state, and then signal the end of the main dialog. @@ -104,6 +118,12 @@ The main dialog has 2 steps: [!code-javascript[step implementations](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/dialogs/mainDialog.js?range=43-55)] +#### [Java](#tab/java) + +**MainDialog.java** + +[!code-java[step implementations](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/MainDialog.java?range=36-54)] + #### [Python](#tab/python) **dialogs\main_dialog.py** @@ -114,7 +134,7 @@ The main dialog has 2 steps: ### The top-level dialog -The top-level dialog has 4 steps: +The top-level dialog has four steps: 1. Ask for the user's name. 1. Ask for the user's age. @@ -137,6 +157,12 @@ In the third (start selection) step, the conversation flow branches, based on th [!code-javascript[step implementations](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/dialogs/topLevelDialog.js?range=32-76&highlight=25-33)] +#### [Java](#tab/java) + +**TopLevelDialog.java** + +[!code-java[step implementations](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/TopLevelDialog.java?range=47-94&highlight=28-34)] + #### [Python](#tab/python) **dialogs\top_level_dialog.py** @@ -147,7 +173,7 @@ In the third (start selection) step, the conversation flow branches, based on th ### The review-selection dialog -The review-selection dialog has 2 steps: +The review-selection dialog has two steps: 1. Ask the user to choose a company to review or `done` to finish. - If the dialog was started with any initial information, the information is available through the _options_ property of the waterfall step context. The review-selection dialog can restart itself, and it uses this to allow the user to choose more than one company to review. @@ -155,7 +181,7 @@ The review-selection dialog has 2 steps: - A `done` choice is added to allow the user to exit the loop early. 1. Repeat this dialog or exit, as appropriate. - If the user chose a company to review, add it to their list. - - If the user has chosen 2 companies or they chose to exit, end the dialog and return the collected list. + - If the user has chosen two companies or they chose to exit, end the dialog and return the collected list. - Otherwise, restart the dialog, initializing it with the contents of their list. #### [C#](#tab/csharp) @@ -170,6 +196,12 @@ The review-selection dialog has 2 steps: [!code-javascript[step implementations](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/dialogs/reviewSelectionDialog.js?range=33-78&highlight=39-45)] +#### [Java](#tab/java) + +**ReviewSelectionDialog.java** + +[!code-java[step implementations](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/ReviewSelectionDialog.java?range=48-99&highlight=46-51)] + #### [Python](#tab/python) **dialogs/review_selection_dialog.py** @@ -183,7 +215,7 @@ The review-selection dialog has 2 steps: The _dialog bot_ class extends the activity handler, and it contains the logic for running the dialogs. The _dialog and welcome bot_ class extends the dialog bot to also welcome a user when they join the conversation. -The bot's turn handler repeats the conversation flow defined by the 3 dialogs. +The bot's turn handler repeats the conversation flow defined by the three dialogs. When it receives a message from the user: 1. It runs the main dialog. @@ -204,6 +236,12 @@ When it receives a message from the user: [!code-javascript[onMessage](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/bots/dialogBot.js?range=24-32&highlight=4-5)] [!code-javascript[run](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/bots/dialogBot.js?range=35-44&highlight=7-9)] +### [Java](#tab/java) + +**DialogBot.java** + +[!code-java[Overrides](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/DialogBot.java?range=40-58&highlight=7-8,18)] + ### [Python](#tab/python) **bots/dialog_bot.py** @@ -224,38 +262,45 @@ Create and register services as needed: **Startup.cs** -[!code-csharp[ConfigureServices](~/../botbuilder-samples/samples/csharp_dotnetcore/43.complex-dialog/Startup.cs?range=18-37)] +[!code-csharp[ConfigureServices](~/../botbuilder-samples/samples/csharp_dotnetcore/43.complex-dialog/Startup.cs?range=16-35)] ### [JavaScript](#tab/javascript) **index.js** -[!code-javascript[ConfigureServices](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/index.js?range=26-43)] +[!code-javascript[Create adapter, memory, state, dialog, and bot](~/../botbuilder-samples/samples/javascript_nodejs/43.complex-dialog/index.js?range=45-59)] + +### [Java](#tab/java) + +**Application.java** + +[!code-java[ConfigureServices](~/../botbuilder-samples/samples/java_springboot/43.complex-dialog/src/main/java/com/microsoft/bot/sample/complexdialog/Application.java?range=52-59)] ### [Python](#tab/python) **app.py** -[!code-python[ConfigureServices](~/../botbuilder-samples/samples/python/43.complex-dialog/app.py?range=29-32)] -[!code-python[ConfigureServices](~/../botbuilder-samples/samples/python/43.complex-dialog/app.py?range=70-77)] +[!code-python[ConfigureServices](~/../botbuilder-samples/samples/python/43.complex-dialog/app.py?range=30-33)] + +[!code-python[ConfigureServices](~/../botbuilder-samples/samples/python/43.complex-dialog/app.py?range=71-78)] --- > [!NOTE] -> Memory storage is used for testing purposes only and is not intended for production use. +> Memory storage is used for testing purposes only and isn't intended for production use. > Be sure to use a persistent type of storage for a production bot. -## To test the bot +## Test the bot -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). 1. Run the sample locally on your machine. -1. Start the emulator, connect to your bot, and send messages as shown below. +1. Start the Emulator, connect to your bot, and send messages as shown below. -![test complex dialog sample](~/media/emulator-v4/test-complex-dialog.png) + :::image type="content" source="../media/emulator-v4/test-complex-dialog.png" alt-text="Example transcript from a conversation with the complex dialog bot."::: ## Additional resources -For an introduction on how to implement a dialog, see [implement sequential conversation flow][simple-dialog], which uses a single waterfall dialog and a few prompts to create a simple interaction that asks the user a series of questions. +For an introduction on how to implement a dialog, see [implement sequential conversation flow][simple-dialog], which uses a single waterfall dialog and a few prompts to ask the user a series of questions. The Dialogs library includes basic validation for prompts. You can also add custom validation. For more information, see [gather user input using a dialog prompt][dialog-prompts]. @@ -267,8 +312,6 @@ For more information, see [reuse dialogs][component-dialogs]. > [!div class="nextstepaction"] > [Reuse dialogs](bot-builder-compositcontrol.md) - - [concept-basics]: bot-builder-basics.md [concept-state]: bot-builder-concept-state.md [concept-dialogs]: bot-builder-concept-dialog.md @@ -277,6 +320,7 @@ For more information, see [reuse dialogs][component-dialogs]. [dialog-prompts]: bot-builder-prompts.md [component-dialogs]: bot-builder-compositcontrol.md -[cs-sample]: https://aka.ms/cs-complex-dialog-sample -[js-sample]: https://aka.ms/js-complex-dialog-sample -[python-sample]: https://aka.ms/python-complex-dialog-sample +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/43.complex-dialog +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/43.complex-dialog +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/43.complex-dialog +[python-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/43.complex-dialog diff --git a/articles/v4sdk/bot-builder-dialog-manage-conversation-flow.md b/articles/v4sdk/bot-builder-dialog-manage-conversation-flow.md index 2067ce4b8..922cf061f 100644 --- a/articles/v4sdk/bot-builder-dialog-manage-conversation-flow.md +++ b/articles/v4sdk/bot-builder-dialog-manage-conversation-flow.md @@ -1,46 +1,49 @@ --- -title: Implement sequential conversation flow - Bot Service -description: Learn how to manage a simple conversation flow with dialogs in the Bot Framework SDK. -keywords: simple conversation flow, sequential conversation flow, dialogs, prompts, waterfalls, dialog set +title: Implement sequential conversation flow +description: Learn how to manage linear conversation flow with dialogs in the Bot Framework SDK. author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/28/2020 +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Implement sequential conversation flow -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Gathering information by posing questions is one of the main ways a bot interacts with users. The dialogs library provides useful built-in features such as *prompt* classes that make it easy to ask questions and validate the response to make sure it matches a specific data type or meets custom validation rules. +Gathering information by posing questions is one of the main ways a bot interacts with users. The dialogs library provides useful built-in features such as _prompt_ classes that make it easy to ask questions and validate the response to make sure it matches a specific data type or meets custom validation rules. -You can manage simple and complex conversation flows using the dialogs library. In a simple interaction, the bot runs through a fixed sequence of steps, and the conversation finishes. In general, a dialog is useful when the bot needs to gather information from the user. This topic details how to implement simple conversation flow by creating prompts and calling them from a waterfall dialog. +You can manage linear and more complex conversation flows using the dialogs library. In a linear interaction, the bot runs through a fixed sequence of steps, and the conversation finishes. A dialog is useful when the bot needs to gather information from the user. -> [!TIP] -> For examples of how to write your own prompts without using the dialogs library, see the [Create your own prompts to gather user input](bot-builder-primitive-prompts.md) article. +This article shows how to implement linear conversation flow by creating prompts and calling them from a waterfall dialog. +For examples of how to write your own prompts without using the dialogs library, see the [Create your own prompts to gather user input](bot-builder-primitive-prompts.md) article. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Knowledge of [bot basics][concept-basics], [managing state][concept-state], and the [dialogs library][concept-dialogs]. -- A copy of the **multi-turn prompt** sample in either [**C#**][cs-sample], [**JavaScript**][js-sample], or [**Python**][python-sample]. +- A copy of the **Multi turn prompts** sample in [C#][cs-sample], [JavaScript][js-sample], [Java][java-sample], or [Python][python-sample]. ## About this sample -In the multi-turn prompt sample, we use a waterfall dialog, a few prompts, and a component dialog to create a simple interaction that asks the user a series of questions. The code uses a dialog to cycle through these steps: +The multi-turn prompts sample uses a waterfall dialog, a few prompts, and a component dialog to create a linear interaction that asks the user a series of questions. The code uses a dialog to cycle through these steps: | Steps | Prompt type | |:-------------|:-------------| | Ask the user for their mode of transportation | Choice prompt | | Ask the user for their name | Text prompt | | Ask the user if they want to provide their age | Confirm prompt | -| If they answered yes, asks for their age | Number prompt with validation to only accept ages greater than 0 and less than 150 | -| If they're not using Microsoft Teams, ask them for a profile picture | Attachment prompt with validation to allow a missing attachment | -| Asks if the collected information is "ok" | Reuse Confirm prompt | +| If they answered yes, ask for their age | Number prompt, with validation to only accept ages greater than 0 and less than 150 | +| If they're not using Microsoft Teams, ask them for a profile picture | Attachment prompt, with validation to allow a missing attachment | +| Ask if the collected information is "ok" | Reuse Confirm prompt | -Finally, if they answered yes, display the collected information; otherwise, tell the user that their information will not be kept. +Finally, if they answered yes, display the collected information; otherwise, tell the user that their information won't be kept. ## Create the main dialog @@ -48,27 +51,27 @@ Finally, if they answered yes, display the collected information; otherwise, tel To use dialogs, install the **Microsoft.Bot.Builder.Dialogs** NuGet package. -The bot interacts with the user via `UserProfileDialog`. When we create the bot's `DialogBot` class, we will set the `UserProfileDialog` as its main dialog. The bot then uses a `Run` helper method to access the dialog. +The bot interacts with the user via `UserProfileDialog`. When creating the bot's `DialogBot` class, the `UserProfileDialog` is set as its main dialog. The bot then uses a `Run` helper method to access the dialog. -![user profile dialog](media/user-profile-dialog.png) +:::image type="content" source="media/user-profile-dialog.png" alt-text="Class diagram for the C# sample."::: **Dialogs\UserProfileDialog.cs** -We begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has 7 steps. +Begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has seven steps. -In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they are used. +In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they're used. -[!code-csharp[Constructor snippet](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=21-48)] +[!code-csharp[Constructor snippet](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=20-47)] -Next, we implement the steps that the dialog uses. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step using `stepContext.Result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. +Next, add the steps that the dialog uses to prompt for input. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step using `stepContext.Result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input. Then it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. -You should always return a non-null `DialogTurnResult` from a waterfall step. If you do not, your dialog may not work as designed. Here we show the implementation for the `NameStepAsync` in the waterfall dialog. +You should always return a non-null `DialogTurnResult` from a waterfall step. If you don't, your dialog may not work as designed. Shown below is the implementation for `NameStepAsync` in the waterfall dialog. -[!code-csharp[Name step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=62-67)] +[!code-csharp[Name step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=61-66)] -In `AgeStepAsync`, we specify a retry prompt for when the user's input fails to validate, either because it is in a format that the prompt can not parse, or the input fails a validation criteria. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to re-prompt the user for input. +In `AgeStepAsync`, specify a retry prompt for when the user's input fails to validate, either because it's in a format that the prompt can't parse, or the input fails a validation criteria. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to reprompt the user for input. -[!code-csharp[Age step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=80-99&highlight=10)] +[!code-csharp[Age step](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=79-98&highlight=10)] **UserProfile.cs** @@ -78,33 +81,33 @@ The user's mode of transportation, name, and age are saved in an instance of the **Dialogs\UserProfileDialog.cs** -In the last step, we check the `stepContext.Result` returned by the dialog called in the previous waterfall step. If the return value is true, we use the user profile accessor to get and update the user profile. To get the user profile, we call the `GetAsync` method, and then set the values of the `userProfile.Transport`, `userProfile.Name`, `userProfile.Age` and `userProfile.Picture` properties. Finally, we summarize the information for the user before calling `EndDialogAsync` which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. +In the last step, check the `stepContext.Result` returned by the dialog called in the previous waterfall step. If the return value is true, the user profile accessor gets and updates the user profile. To get the user profile, call `GetAsync` and then set the values of the `userProfile.Transport`, `userProfile.Name`, `userProfile.Age` and `userProfile.Picture` properties. Finally, summarize the information for the user before calling `EndDialogAsync`, which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. -[!code-csharp[SummaryStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=137-179&highlight=5-11,41-42)] +[!code-csharp[SummaryStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=136-178&highlight=5-11,41-42)] # [JavaScript](#tab/javascript) To use dialogs, your project needs to install the **botbuilder-dialogs** npm package. -The bot interacts with the user via a `UserProfileDialog`. When we create the bot's `DialogBot`, we will set the `UserProfileDialog` as its main dialog. The bot then uses a `run` helper method to access the dialog. +The bot interacts with the user via a `UserProfileDialog`. When creating the bot's `DialogBot`, the `UserProfileDialog` is set as its main dialog. The bot then uses a `run` helper method to access the dialog. -![user profile dialog](media/user-profile-dialog-js.png) +:::image type="content" source="media/user-profile-dialog-js.png" alt-text="Class diagram for the JavaScript sample."::: **dialogs/userProfileDialog.js** -We begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has 7 steps. +Begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has seven steps. -In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they are used. +In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they're used. [!code-javascript[Constructor snippet](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=29-51)] -Next, we implement the steps that the dialog uses. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step from the step context, in this case by using `step.result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. +Next, add the steps that the dialog uses to prompt for input. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step from the step context, in this case by using `step.result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input. Then it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. -You should always return a non-null `DialogTurnResult` from a waterfall step. If you do not, your dialog may not work as designed. Here we show the implementation for the `nameStep` in the waterfall dialog. +You should always return a non-null `DialogTurnResult` from a waterfall step. If you don't, your dialog may not work as designed. Shown below is the implementation for the `nameStep` in the waterfall dialog. [!code-javascript[name step](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=79-82)] -In `ageStep`, we specify a retry prompt for when the user's input fails to validate, either because it is in a format that the prompt can not parse, or the input fails a validation criteria, specified in the constructor above. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to re-prompt the user for input. +In `ageStep`, specify a retry prompt for when the user's input fails to validate, either because it's in a format that the prompt can't parse, or the input fails a validation criteria, specified in the constructor above. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to reprompt the user for input. [!code-javascript[age step](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=94-105&highlight=5)] @@ -116,43 +119,79 @@ The user's mode of transportation, name, and age are saved in an instance of the **dialogs/userProfileDialog.js** -In the last step, we check the `step.result` returned by the dialog called in the previous waterfall step. If the return value is true, we use the user profile accessor to get and update the user profile. To get the user profile, we call the `get` method, and then set the values of the `userProfile.transport`, `userProfile.name`, `userProfile.age` and `userProfile.picture` properties. Finally, we summarize the information for the user before calling `endDialog` which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. +In the last step, check the `step.result` returned by the dialog called in the previous waterfall step. If the return value is true, the user profile accessor gets and updates the user profile. To get the user profile, call `get`, and then set the values of the `userProfile.transport`, `userProfile.name`, `userProfile.age` and `userProfile.picture` properties. Finally, summarize the information for the user before calling `endDialog`, which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. [!code-javascript[summary step](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=137-167&highlight=3-9,29-30)] **Create the extension method to run the waterfall dialog** -We've defined a `run` helper method inside `userProfileDialog` that we will use to create and access the dialog context. Here, `accessor` is the state property accessor for the dialog state property, and `this` is the user profile component dialog. Since component dialogs define an inner dialog set, we must create an outer dialog set that's visible to the message handler code and use that to create a dialog context. +A `run` helper method, defined inside `userProfileDialog`, is used to create and access the dialog context. Here, `accessor` is the state property accessor for the dialog state property, and `this` is the user profile component dialog. Since component dialogs define an inner dialog set, an outer dialog set must be created that's visible to the message handler code and used to create a dialog context. The dialog context is created by calling the `createContext` method, and is used to interact with the dialog set from within the bot's turn handler. The dialog context includes the current turn context, the parent dialog, and the dialog state, which provides a method for preserving information within the dialog. -The dialog context allows you to start a dialog with the string ID, or continue the current dialog (such as a waterfall dialog that has multiple steps). The dialog context is passed through to all the bot's dialogs and waterfall steps. +The dialog context allows you to start a dialog with the string ID, or continue the current dialog (like a waterfall dialog that has multiple steps). The dialog context is passed through to all the bot's dialogs and waterfall steps. [!code-javascript[run method](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=59-68)] +# [Java](#tab/java) + +The bot interacts with the user via `UserProfileDialog`. When creating the bot's `DialogBot` class, the `UserProfileDialog` is set as its main dialog. The bot then uses a `Run` helper method to access the dialog. + +:::image type="content" source="media/user-profile-dialog-java.png" alt-text="Class diagram for the Java sample."::: + +**UserProfileDialog.java** + +Begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has seven steps. + +In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they're used. + +[!code-java[Constructor snippet](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=34-59)] + +Next, add the steps that the dialog uses to prompt for input. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step using `stepContext.getResult()`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input. Then it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. + +You should always return a non-null `DialogTurnResult` from a waterfall step. If you don't, your dialog may not work as designed. Shown below is the implementation for `nameStep` in the waterfall dialog. + +[!code-java[Name step](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=71-77)] + +In `ageStep`, specify a retry prompt for when the user's input fails to validate, either because it's in a format that the prompt can't parse, or the input fails a validation criteria. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to reprompt the user for input. + +[!code-java[Age step](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=92-105&highlight=7)] + +**UserProfile.java** + +The user's mode of transportation, name, and age are saved in an instance of the `UserProfile` class. + +[!code-java[UserProfile class](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfile.java?range=8=16)] + +**UserProfileDialog.java** + +In the last step, check the `stepContext.Result` returned by the dialog called in the previous waterfall step. If the return value is true, the user profile accessor gets and updates the user profile. To get the user profile, call `get` and then set the values of the `userProfile.Transport`, `userProfile.Name`, `userProfile.Age` and `userProfile.Picture` properties. Finally, summarize the information for the user before calling `endDialog`, which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. + +[!code-java[SummaryStep](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java??range=142-191&highlight=3-8,43)] + # [Python](#tab/python) To use dialogs, install the **botbuilder-dialogs** and **botbuilder-ai** PyPI packages by running `pip install botbuilder-dialogs` and `pip install botbuilder-ai` from a terminal. -The bot interacts with the user via `UserProfileDialog`. When we create the bot's `DialogBot` class, we will set the `UserProfileDialog` as its main dialog. The bot then uses a `run_dialog` helper method to access the dialog. +The bot interacts with the user via `UserProfileDialog`. When the bot's `DialogBot` class is created, the `UserProfileDialog` is set as its main dialog. The bot then uses a `run_dialog` helper method to access the dialog. -![user profile dialog](media/user-profile-dialog-python.png) +:::image type="content" source="media/user-profile-dialog-python.png" alt-text="Class diagram for the Python sample."::: **dialogs\user_profile_dialog.py** -We begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has 7 steps. +Begin by creating the `UserProfileDialog` that derives from the `ComponentDialog` class, and has seven steps. -In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they are used. +In the `UserProfileDialog` constructor, create the waterfall steps, prompts and the waterfall dialog, and add them to the dialog set. The prompts need to be in the same dialog set in which they're used. [!code-python[Constructor snippet](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=26-57)] -Next, we implement the steps that the dialog uses. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step using `step_context.result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input; second, it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. +Next, add the steps that the dialog uses to prompt for input. To use a prompt, call it from a step in your dialog and retrieve the prompt result in the following step using `step_context.result`. Behind the scenes, prompts are a two-step dialog. First, the prompt asks for input. Then it returns the valid value, or starts over from the beginning with a reprompt until it receives a valid input. -You should always return a non-null `DialogTurnResult` from a waterfall step. If you do not, your dialog may not work as designed. Here we show the implementation for the `name_step` in the waterfall dialog. +You should always return a non-null `DialogTurnResult` from a waterfall step. If you don't, your dialog may not work as designed. Here you can see the implementation for the `name_step` in the waterfall dialog. [!code-python[name step](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=73-79)] -In `age_step`, we specify a retry prompt for when the user's input fails to validate, either because it is in a format that the prompt can not parse, or the input fails a validation criteria, specified in the constructor above. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to re-prompt the user for input +In `age_step`, specify a retry prompt for when the user's input fails to validate, either because it's in a format that the prompt can't parse, or the input fails a validation criteria, specified in the constructor above. In this case, if no retry prompt was provided, the prompt will use the initial prompt text to reprompt the user for input [!code-python[age step](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=100-116)] @@ -164,15 +203,15 @@ The user's mode of transportation, name, and age are saved in an instance of the **dialogs\user_profile_dialog.py** -In the last step, we check the `step_context.result` returned by the dialog called in the previous waterfall step. If the return value is true, we use the user profile accessor to get and update the user profile. To get the user profile, we call the `get` method, and then set the values of the `user_profile.transport`, `user_profile.name`, and `user_profile.age` properties. Finally, we summarize the information for the user before calling `end_dialog` which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. +In the last step, check the `step_context.result` returned by the dialog called in the previous waterfall step. If the return value is true, the user profile accessor gets and updates the user profile. To get the user profile, call `get`, and then set the values of the `user_profile.transport`, `user_profile.name`, and `user_profile.age` properties. Finally, summarize the information for the user before calling `end_dialog`, which ends the dialog. Ending the dialog pops it off the dialog stack and returns an optional result to the dialog's parent. The parent is the dialog or method that started the dialog that just ended. [!code-python[summary step](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=166-204)] **Create the extension method to run the waterfall dialog** -We've defined a `run_dialog()` helper method inside **helpers\dialog_helper.py** that we will use to create and access the dialog context. Here, `accessor` is the state property accessor for the dialog state property, and `dialog` is the user profile component dialog. Since component dialogs define an inner dialog set, we must create an outer dialog set that's visible to the message handler code and use that to create a dialog context. +A `run_dialog()` helper method is defined in **helpers\dialog_helper.py** that's used to create and access the dialog context. Here, `accessor` is the state property accessor for the dialog state property, and `dialog` is the user profile component dialog. Since component dialogs define an inner dialog set, an outer dialog set must be created that's visible to the message handler code and use that to create a dialog context. -The dialog context is created by calling the `create_context` method, and is used to interact with the dialog set from within the bot's turn handler. The dialog context includes the current turn context, the parent dialog, and the dialog state, which provides a method for preserving information within the dialog. +Create the dialog context by calling the `create_context`, which is used to interact with the dialog set from within the bot's turn handler. The dialog context includes the current turn context, the parent dialog, and the dialog state, which provides a method for preserving information within the dialog. The dialog context allows you to start a dialog with the string ID, or continue the current dialog (such as a waterfall dialog that has multiple steps). The dialog context is passed through to all the bot's dialogs and waterfall steps. @@ -186,7 +225,7 @@ The dialog context allows you to start a dialog with the string ID, or continue **Bots\DialogBot.cs** -The `OnMessageActivityAsync` handler uses the `RunAsync` method to start or continue the dialog. In `OnTurnAsync`, we use the bot's state management objects to persist any state changes to storage. The `ActivityHandler.OnTurnAsync` method calls the various activity handler methods, such as `OnMessageActivityAsync`. In this way, we are saving state after the message handler completes but before the turn itself completes. +The `OnMessageActivityAsync` handler uses the `RunAsync` method to start or continue the dialog. `OnTurnAsync` uses the bot's state management objects to persist any state changes to storage. The `ActivityHandler.OnTurnAsync` method calls the various activity handler methods, such as `OnMessageActivityAsync`. In this way, the state is saved after the message handler completes but before the turn itself completes. [!code-csharp[overrides](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Bots/DialogBot.cs?range=33-48&highlight=5-7)] @@ -194,7 +233,7 @@ The `OnMessageActivityAsync` handler uses the `RunAsync` method to start or cont The `onMessage` method registers a listener that calls the dialog's `run` method to start or continue the dialog. -Separately, the bot overrides the `ActivityHandler.run` method to save conversation and user state to storage. In this way, we are saving state after the message handler completes but before the turn itself completes. +Separately, the bot overrides the `ActivityHandler.run` method to save conversation and user state to storage. In this way, the state is saved after the message handler completes but before the turn itself completes. **bots/dialogBot.js** @@ -202,18 +241,27 @@ Separately, the bot overrides the `ActivityHandler.run` method to save conversat [!code-javascript[override](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/bots/dialogBot.js?range=34-43&highlight=7-9)] +# [Java](#tab/java) + +**DialogBot.java** + +The `onMessageActivity` handler uses the `run` method to start or continue the dialog. `onTurn` uses the bot's state management objects to persist any state changes to storage. The `ActivityHandler.onTurn` method calls the various activity handler methods, such as `onMessageActivity`. In this way, the state is saved after the message handler completes but before the turn itself completes. + +[!code-java[overrides](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/DialogBot.java?range=40-58&highlight=6-8)] + # [Python](#tab/python) -The `on_message_activity` handler uses the helper method to start or continue the dialog. In `on_turn`, we use the bot's state management objects to persist any state changes to storage. The `on_message_activity` method gets called last after other defined handlers are run, such as `on_turn`. In this way, we are saving state after the message handler completes but before the turn itself completes. +The `on_message_activity` handler uses the helper method to start or continue the dialog. The `on_turn` method uses the bot's state management objects to persist any state changes to storage. The `on_message_activity` method gets called last after other defined handlers are run, such as `on_turn`. In this way, the state is saved after the message handler completes but before the turn itself completes. **bots\dialog_bot.py** + [!code-python[overrides](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/bots/dialog_bot.py?range=39-51&highlight=4-6)] --- ## Register services for the bot -This bot uses the following _services_. +This bot uses the following services: - Basic services for a bot: a credential provider, an adapter, and the bot implementation. - Services for managing state: storage, user state, and conversation state. @@ -223,54 +271,62 @@ This bot uses the following _services_. **Startup.cs** -We register services for the bot in `Startup`. These services are available to other parts of the code through dependency injection. +Register services for the bot in `Startup`. These services are available to other parts of the code through dependency injection. -[!code-csharp[ConfigureServices](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Startup.cs?range=17-39)] +[!code-csharp[ConfigureServices](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Startup.cs?range=15-37)] # [JavaScript](#tab/javascript) **index.js** -We register services for the bot in `index.js`. +Register services for the bot in `index.js`. + +[!code-javascript[Create adapter, memory, state, dialog, and bot](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/index.js?range=35-73)] -[!code-javascript[overrides](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/index.js?range=19-59)] +# [Java](#tab/java) + +**Application.java** + +Spring will provide the ConversationState, UserState, and Dialog via dependency injection. Override the getBot and return an instance of the DialogBot. + +[!code-java[ConfigureServices](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/Application.java?range=52-59)] # [Python](#tab/python) -We register services for the bot in `app.py`. +Register services for the bot in `app.py`. -[!code-python[configure services](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/app.py?range=27-76)] +[!code-python[configure services](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/app.py?range=28-77)] --- > [!NOTE] -> Memory storage is used for testing purposes only and is not intended for production use. +> Memory storage is used for testing purposes only and isn't intended for production use. > Be sure to use a persistent type of storage for a production bot. -## To test the bot +## Test your bot -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). 1. Run the sample locally on your machine. -1. Start the emulator, connect to your bot, and send messages as shown below. +1. Start the Emulator, connect to your bot, and send messages as shown below. -![Sample run of the multi-turn prompt dialog](../media/emulator-v4/multi-turn-prompt.png) +:::image type="content" source="../media/emulator-v4/multi-turn-prompt.png" alt-text="An example transcript of a conversation with the multi-turn prompt bot."::: ## Additional information ### About dialog and bot state -In this bot, we've defined two state property accessors: +In this bot, two state property accessors are defined: -- One created within conversation state for the dialog state property. The dialog state tracks where the user is within the dialogs of a dialog set, and it is updated by the dialog context, such as when we call the begin dialog or continue dialog methods. -- One created within user state for the user profile property. The bot uses this to track information it has about the user, and we explicitly manage this state in our dialog code. +- One created within conversation state for the dialog state property. The dialog state tracks where the user is within the dialogs of a dialog set, and it's updated by the dialog context, such as when the _begin dialog_ or _continue dialog_ methods are called. +- One created within user state for the user profile property. The bot uses this to track information it has about the user, and you must explicitly manage this state in the dialog code. -The _get_ and _set_ methods of a state property accessor get and set the value of the property in the state management object's cache. The cache is populated the first time the value of a state property is requested in a turn, but it must be persisted explicitly. In order to persist changes to both of these state properties, we call the _save changes_ method of the corresponding state management object. +The _get_ and _set_ methods of a state property accessor get and set the value of the property in the state management object's cache. The cache is populated the first time the value of a state property is requested in a turn, but it must be persisted explicitly. In order to persist changes to both of these state properties, a call to the _save changes_ method, of the corresponding state management object, is performed. -This sample updates the user profile state from within the dialog. This practice can work for a simple bot, but will not work if you want to reuse a dialog across bots. +This sample updates the user profile state from within the dialog. This practice can work for some bots, but it won't work if you want to reuse a dialog across bots. There are various options for keeping dialog steps and bot state separate. For example, once your dialog gathers complete information, you can: -- Use the end dialog method to provide the collected data as return value back to the parent context. This can be the bot's turn handler or an earlier active dialog on the dialog stack. This is how the prompt classes are designed. +- Use the _end dialog_ method to provide the collected data as return value back to the parent context. This can be the bot's turn handler or an earlier active dialog on the dialog stack and it's how the prompt classes are designed. - Generate a request to an appropriate service. This might work well if your bot acts as a front end to a larger service. ### Definition of a prompt validator method @@ -279,23 +335,31 @@ There are various options for keeping dialog steps and bot state separate. For e **UserProfileDialog.cs** -Below is an example validator code for the `AgePromptValidatorAsync` method definition. `promptContext.Recognized.Value` contains the parsed value, which is an integer here for the number prompt. `promptContext.Recognized.Succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value was not accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. Note that you can change the value in the validator per your scenario. +Below is a validator code example for the `AgePromptValidatorAsync` method definition. `promptContext.Recognized.Value` contains the parsed value, which is an integer here for the number prompt. `promptContext.Recognized.Succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value wasn't accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. You can change the value in the validator per your scenario. -[!code-csharp[prompt validator method](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=181-185)] +[!code-csharp[prompt validator method](~/../botbuilder-samples/samples/csharp_dotnetcore/05.multi-turn-prompt/Dialogs/UserProfileDialog.cs?range=180-184)] # [JavaScript](#tab/javascript) **dialogs\userProfileDialog.js** -Below is an example validator code for the `agePromptValidator` method definition. `promptContext.recognized.value` contains the parsed value, which is an integer here for the number prompt. `promptContext.recognized.succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value was not accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. Note that you can change the value in the validator per your scenario. +Below is a validator code example for the `agePromptValidator` method definition. `promptContext.recognized.value` contains the parsed value, which is an integer here for the number prompt. `promptContext.recognized.succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value wasn't accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. You can change the value in the validator per your scenario. + +[!code-javascript[age prompt validator](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=169-172)] + +# [Java](#tab/java) -[!code-javascript[prompt validator method](~/../botbuilder-samples/samples/javascript_nodejs/05.multi-turn-prompt/dialogs/userProfileDialog.js?range=169-172)] +**UserProfileDialog.java** + +Below is a validator code example for the `agePromptValidator` method definition. `promptContext.getRecognized().getValue()` contains the parsed value, which is an integer here for the number prompt. `promptContext.getRecognized().getSucceeded()` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that it didn't accept the value. The prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. You can change the value in the validator per your scenario. + +[!code-csharp[prompt validator method](~/../botbuilder-samples/samples/java_springboot/05.multi-turn-prompt/src/main/java/com/microsoft/bot/sample/multiturnprompt/UserProfileDialog.java?range=193-201)] # [Python](#tab/python) **dialogs/user_profile_dialog.py** -Below is an example validator code for the `age_prompt_validator` method definition. `prompt_context.recognized.value` contains the parsed value, which is an integer here for the number prompt. `prompt_context.recognized.succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value was not accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. Note that you can change the value in the validator per your scenario. +Below is a validator code example for the `age_prompt_validator` method definition. `prompt_context.recognized.value` contains the parsed value, which is an integer here for the number prompt. `prompt_context.recognized.succeeded` indicates whether the prompt was able to parse the user's input or not. The validator should return false to indicate that the value wasn't accepted and the prompt dialog should reprompt the user; otherwise, return true to accept the input and return from the prompt dialog. You can change the value in the validator per your scenario. [!code-python[prompt validator method](~/../botbuilder-samples/samples/python/05.multi-turn-prompt/dialogs/user_profile_dialog.py?range=207-212)] @@ -306,15 +370,11 @@ Below is an example validator code for the `age_prompt_validator` method definit > [!div class="nextstepaction"] > [Add natural language understanding to your bot](bot-builder-howto-v4-luis.md) - - [concept-basics]: bot-builder-basics.md [concept-state]: bot-builder-concept-state.md [concept-dialogs]: bot-builder-concept-dialog.md -[prompting]: bot-builder-prompts.md -[component-dialogs]: bot-builder-compositcontrol.md - -[cs-sample]: https://aka.ms/cs-multi-prompts-sample -[js-sample]: https://aka.ms/js-multi-prompts-sample -[python-sample]: https://aka.ms/python-multi-prompts-sample +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/05.multi-turn-prompt +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/05.multi-turn-prompt +[python-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/05.multi-turn-prompt diff --git a/articles/v4sdk/bot-builder-dialogs-greeting.md b/articles/v4sdk/bot-builder-dialogs-greeting.md deleted file mode 100644 index 656a71289..000000000 --- a/articles/v4sdk/bot-builder-dialogs-greeting.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: Implement a greeting dialog - Bot Service -description: Use a dialog to greet a user when they join a conversation. -keywords: greeting, dialogs, conversation flow, dialog set -author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Implement a greeting dialog - -[!INCLUDE [pre-release-label](../includes/pre-release-label.md)] - -You can use a dialog to welcome a user to a conversation. - -For more information about welcoming users, see how to [send welcome messages to users][send-welcome]. - -## Prerequisites - -- Knowledge of [managing state][concept-state], the [dialogs library][concept-dialogs], how to [manage conversations][simple-flow], and how to [gather user input using a dialog prompt][prompting]. -- A copy of the ??? sample in either [**CSharp**][cs-sample] or [**JavaScript**][js-sample]. - -## \ [as in to do X, do these things] - - - -## About the sample code - - - -- Additional packages needed (AI.Luis, Dialogs, etc.) - - - -## To test the bot - -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). -1. Run the sample locally on your machine. -1. Start the emulator, connect to your bot, and send messages as shown below. - -TODO: Take new screenshot. - - - -## Discussion [optional] - - - -## Addition information - - - -- link to the "about the core bot" landing page for the overall sample (for CoreBot-derived articles). - -## Next steps - -> [!div class="nextstepaction"] -> [Handle user interruptions](bot-builder-howto-handle-user-interrupt.md) - - - -[concept-basics]: bot-builder-basics.md -[concept-state]: bot-builder-concept-state.md -[concept-dialogs]: bot-builder-concept-dialog.md - -[send-welcome]: bot-builder-send-welcome-message.md - -[simple-flow]: bot-builder-dialog-manage-conversation-flow.md -[prompting]: bot-builder-prompts.md -[component-dialogs]: bot-builder-compositcontrol.md - -[cs-sample]: ??? -[js-sample]: ??? diff --git a/articles/v4sdk/bot-builder-howto-add-media-attachments.md b/articles/v4sdk/bot-builder-howto-add-media-attachments.md index 4636213fd..57b17f65b 100644 --- a/articles/v4sdk/bot-builder-howto-add-media-attachments.md +++ b/articles/v4sdk/bot-builder-howto-add-media-attachments.md @@ -1,118 +1,164 @@ --- -title: Add media to messages - Bot Service -description: Learn how to add media to messages using the Bot Framework SDK. -keywords: media, messages, images, audio, video, files, MessageFactory, rich cards, messages, adaptive cards, hero card, suggested actions -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 02/03/2020 -monikerRange: 'azure-bot-service-4.0' +title: Send media attachments with the Bot Framework SDK +description: Learn how to add images, video, audio, files, and other media attachments to messages sent using the Bot Framework SDK. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen +monikerRange: 'azure-bot-service-4.0' --- -# Add media to messages +# Send media attachments with Bot Framework SDK -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Messages exchanged between user and bot can contain media attachments, such as images, video, audio, and files. The Bot Framework SDK supports the task of sending rich messages to the user. To determine the type of rich messages a channel (Facebook, Skype, Slack, etc.) supports, consult the channel's documentation for information about limitations. +Messages exchanged between user and bot can contain media attachments, such as images, video, audio, and files. The Bot Framework SDK supports the task of sending rich messages to the user. To determine the type of rich messages a channel (Facebook, Slack, and so on) supports, consult the channel's documentation for information about limitations. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Knowledge of [bot basics](bot-builder-basics.md). - The code in this article is based on the following samples: - - | Sample code | C# | JS | Python | - | :------ | :----- | :---| :---| - | Cards | [C# sample](https://aka.ms/bot-cards-sample-code) | [JS sample](https://aka.ms/bot-cards-js-sample-code) |[Python sample](https://aka.ms/bot-cards-python-sample-code) | - | Attachments | [C# sample](https://aka.ms/bot-attachments-sample-code) | [JS sample](https://aka.ms/bot-attachments-sample-code-js) | [Python sample](https://aka.ms/bot-media-attachments-python-sample-code) | - | Suggested actions | [C# sample](https://aka.ms/SuggestedActionsCSharp) | [JS sample](https://aka.ms/SuggestedActionsJS) | [Python sample](https://aka.ms/SuggestedActionsPython) | + - **Using cards**: [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/06.using-cards), [JavaScript](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/06.using-cards), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/06.using-cards), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/06.using-cards) + - **Handling attachments**: [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/15.handling-attachments), [JavaScript](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/15.handling-attachments), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/15.handling-attachments), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/15.handling-attachments) + - **Suggested actions**: [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/08.suggested-actions), [JavaScript](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/08.suggested-actions), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/08.suggested-actions), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/08.suggested-actions) ## Send attachments To send the user content like an image or a video, you can add an attachment or list of attachments to a message. -See [design user experience](../bot-service-design-user-experience.md) for examples of available cards. +See [Design the user experience](../bot-service-design-user-experience.md#cards) for examples of available cards. + +See also [What is the size limit of a file transferred using channels?](../bot-service-resources-faq-general.md#what-is-the-size-limit-of-a-file-transferred-using-channels) in the FAQ. ### [C#](#tab/csharp) -The `Attachments` property of the `Activity` object contains an array of `Attachment` objects that represent the media attachments and rich cards attached to the message. To add a media attachment to a message, create an `Attachment` object for the `reply` activity (that was created off the activity with `CreateReply()`) and set the `ContentType`, `ContentUrl`, and `Name` properties. +All of the source code shown in this section is based on the [Handling attachments](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/15.handling-attachments) sample. -The source code shown here is based on the [Handling Attachments](https://aka.ms/bot-attachments-sample-code) sample. +The `Attachments` property of the `Activity` object contains an array of `Attachment` objects that represent the media attachments and rich cards attached to the message. To add a media attachment to a message, create an `Attachment` object for the `reply` activity and set the `ContentType`, `ContentUrl`, and `Name` properties. To create the reply message, define the text and then set up the attachments. Assigning the attachments to the reply is the same for each attachment type, however the various attachments are set up and defined differently, as seen in the following snippets. The code below is setting up the reply for an inline attachment: -**Bots/AttachmentsBot.cs** -[!code-csharp[inline attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=105-106)] +**Bots/AttachmentsBot.cs** + +[!code-csharp[reply inline](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=105-106)] Next, we look at the types of attachments. First is an inline attachment: -**Bots/AttachmentsBot.cs** +**Bots/AttachmentsBot.cs** + [!code-csharp[inline attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=167-178)] Then, an uploaded attachment: -**Bots/AttachmentsBot.cs** -[!code-csharp[uploaded attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=181-214)] +**Bots/AttachmentsBot.cs** + +[!code-csharp[uploaded attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=181-215)] Lastly, an internet attachment: -**Bots/AttachmentsBot.cs** -[!code-csharp[online attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=217-226)] +**Bots/AttachmentsBot.cs** + +[!code-csharp[online attachment](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=218-227)] ### [JavaScript](#tab/javascript) -The source code shown here is based on the [JS Handling Attachments](https://aka.ms/bot-attachments-sample-code-js) sample. +The following source code is from the [Handling attachments](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/15.handling-attachments) sample. To use attachments, include the following libraries in your bot: -**bots/attachmentsBot.js** +**bots/attachmentsBot.js** + [!code-javascript[attachments libraries](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=4)] To create the reply message, define the text and then set up the attachments. Assigning the attachments to the reply is the same for each attachment type, however the various attachments are set up and defined differently, as seen in the following snippets. The code below is setting up the reply for an inline attachment: -**bots/attachmentsBot.js** +**bots/attachmentsBot.js** + [!code-javascript[attachments](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=119,128-129)] To send the user a single piece of content like an image or a video, you can send media in a few different ways. First, as an inline attachment: -**bots/attachmentsBot.js** +**bots/attachmentsBot.js** + [!code-javascript[inline attachments](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=170-179)] Then, an uploaded attachment: -**bots/attachmentsBot.js** +**bots/attachmentsBot.js** + [!code-javascript[uploaded attachments](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=197-215)] Lastly, an internet attachment contained in a URL: -**bots/attachmentsBot.js** +**bots/attachmentsBot.js** + [!code-javascript[internet attachments](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=184-191)] +### [Java](#tab/java) + +The source code shown in this section is based on the [Handling attachments](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/15.handling-attachments) sample. + +The `getAttachments()` method of the `Activity` object contains an array of `Attachment` objects that represent the media attachments and rich cards attached to the message. To add a media attachment to a message, create an `Attachment` object for the `reply` activity and set the `ContentType`, `ContentUrl`, and `Name` properties. + +To create the reply message, define the text and then set up the attachments. Assigning the attachments to the reply is the same for each attachment type, however the various attachments are set up and defined differently, as seen in the following snippets. The code below is setting up the reply for an inline attachment: + +**AttachmentsBot.java** + +[!code-java[reply inline](~/../botbuilder-samples/samples/java_springboot/15.handling-attachments/src/main/java/com/microsoft/bot/sample/attachments/AttachmentsBot.java?range=122-127)] + +Next, we look at the types of attachments. First is an inline attachment: + +**AttachmentsBot.java** + +[!code-java[inline attachment](~/../botbuilder-samples/samples/java_springboot/15.handling-attachments/src/main/java/com/microsoft/bot/sample/attachments/AttachmentsBot.java?range=192-206)] + +Then, an uploaded attachment: + +**AttachmentsBot.java** + +[!code-java[uploaded attachment](~/../botbuilder-samples/samples/java_springboot/15.handling-attachments/src/main/java/com/microsoft/bot/sample/attachments/AttachmentsBot.java?range=209-240)] + +Lastly, an internet attachment: + +**AttachmentsBot.java** + +[!code-java[online attachment](~/../botbuilder-samples/samples/java_springboot/15.handling-attachments/src/main/java/com/microsoft/bot/sample/attachments/AttachmentsBot.java?range=242-250)] + ### [Python](#tab/python) -To create the reply message, define the text and then set up the attachments. Assigning the attachments to the reply is the same for each attachment type, however the various attachments are set up and defined differently, as seen in the following snippets. +The following source code is from the [Handling attachments](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/15.handling-attachments) sample. -The source code shown here is based on the [Handling Attachments](https://aka.ms/bot-media-attachments-python-sample-code) sample. +To create the reply message, define the text and then set up the attachments. Assigning the attachments to the reply is the same for each attachment type, however the various attachments are set up and defined differently, as seen in the following snippets. The code below is setting up the reply for an inline attachment: -**bots/attachments_bot.py** +**bots/attachments_bot.py** + [!code-python[attachments](~/../botbuilder-samples/samples/python/15.handling-attachments/bots/attachments_bot.py?range=112-113)] To send the user a single piece of content like an image or a video, you can send media in a few different ways. First, as an inline attachment: **bots/attachments_bot.py** + [!code-python[inline attachments](~/../botbuilder-samples/samples/python/15.handling-attachments/bots/attachments_bot.py?range=153-170)] Then, an uploaded attachment: **bots/attachments_bot.py** + [!code-python[upload attachments](~/../botbuilder-samples/samples/python/15.handling-attachments/bots/attachments_bot.py?range=172-207)] Lastly, an internet attachment contained in a URL: **bots/attachments_bot.py** + [!code-python[internet attachments](~/../botbuilder-samples/samples/python/15.handling-attachments/bots/attachments_bot.py?range=209-218)] --- @@ -121,54 +167,55 @@ If an attachment is an image, audio, or video, the Connector service will commun ## Send a hero card -Besides simple image or video attachments, you can attach a **hero card**, which allows you to combine images and buttons in one object, and send them to the user. Markdown is supported for most text fields, but support may vary by channel. +Besides simple image or video attachments, you can attach a _hero card_, which allows you to combine images and buttons in one object, and send them to the user. Markdown is supported for most text fields, but support may vary by channel. ### [C#](#tab/csharp) -To compose a message with a hero card and button, you can attach a `HeroCard` to a message. +To compose a message with a hero card and button, you can attach a `HeroCard` object to a message. + +The following source code is from the [Handling attachments](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/15.handling-attachments) sample. -The source code shown here is based on the [Handling Attachments](https://aka.ms/bot-attachments-sample-code) sample. +**Bots/AttachmentsBot.cs** -**Bots/AttachmentsBot.cs** [!code-csharp[Hero card](~/../botbuilder-samples/samples/csharp_dotnetcore/15.handling-attachments/Bots/AttachmentsBot.cs?range=39-58)] ### [JavaScript](#tab/javascript) -To compose a message with a hero card and button, you can attach a `HeroCard` to a message. +To compose a message with a hero card and button, you can attach a `HeroCard` object to a message. -The source code shown here is based on the [JS Handling Attachments](https://aka.ms/bot-attachments-sample-code-js) sample. +The following source code is from the [Handling attachments](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/15.handling-attachments) sample. + +**bots/attachmentsBot.js** -**bots/attachmentsBot.js** [!code-javascript[hero card](~/../botbuilder-samples/samples/javascript_nodejs/15.handling-attachments/bots/attachmentsBot.js?range=147-165)] +### [Java](#tab/java) + +To compose a message with a hero card and button, you can attach a `HeroCard` object to a message. + +The following source code is from the [Handling attachments](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/15.handling-attachments) sample. + +**AttachmentsBot.java** + +[!code-java[Hero card](~/../botbuilder-samples/samples/java_springboot/15.handling-attachments/src/main/java/com/microsoft/bot/sample/attachments/AttachmentsBot.java?range=67-83)] + ### [Python](#tab/python) -To compose a message with a hero card and button, you can attach a `HeroCard` to a message. +To compose a message with a hero card and button, you can attach a `HeroCard` object to a message. -The source code shown here is based on the [Handling Attachments](https://aka.ms/bot-media-attachments-python-sample-code) sample. +The following source code is from the [Handling attachments](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/15.handling-attachments) sample. **bots/attachments_bot.py** + [!code-python[hero card](~/../botbuilder-samples/samples/python/15.handling-attachments/bots/attachments_bot.py?range=125-148)] --- ## Process events within rich cards -To process events within rich cards, use _card action_ objects to specify what should happen when the user clicks a button or taps a section of the card. Each card action has a _type_ and _value_. +To process events within rich cards, use _card action_ objects to specify what should happen when the user selects a button or taps a section of the card. Each card action has a _type_ and _value_ property. -To function correctly, assign an action type to each clickable item on the card. This table lists and describes the available action types and what should be in the associated value property. - -| Type | Description | Value | -| :---- | :---- | :---- | -| openUrl | Opens a URL in the built-in browser. | The URL to open. | -| imBack | Sends a message to the bot, and posts a visible response in the chat. | Text of the message to send. | -| postBack | Sends a message to the bot, and may not post a visible response in the chat. | Text of the message to send. | -| call | Initiates a phone call. | Destination for the phone call in this format: `tel:123123123123`. | -| playAudio | Plays audio. | The URL of the audio to play. | -| playVideo | Plays a video. | The URL of video to play. | -| showImage | Displays an image. | The URL of the image to display. | -| downloadFile | Downloads a file. | The URL of the file to download. | -| signin | Initiates an OAuth signin process. | The URL of the OAuth flow to initiate. | +[!INCLUDE [Table of card action types](../includes/snippet-card-action-types.md)] ## Hero card using various event types @@ -176,46 +223,61 @@ The following code shows examples using various rich card events. ### [C#](#tab/csharp) -For examples of all the available cards, see the [C# cards sample](https://aka.ms/bot-cards-sample-code). +For examples of all the available cards, see the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/06.using-cards) sample. + +**Cards.cs** -**Cards.cs** -[!code-csharp[hero cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=27-40)] +[!code-csharp[GetHeroCard](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=28-41)] -**Cards.cs** -[!code-csharp[cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=91-100)] +**Cards.cs** + +[!code-csharp[GetSigninCard](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=92-101)] ### [JavaScript](#tab/javascript) -For examples of all the available cards, see the [JS cards sample](https://aka.ms/bot-cards-js-sample-code). +For examples of all the available cards, see the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/06.using-cards) sample. + +**dialogs/mainDialog.js** + +[!code-javascript[createHeroCard](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=214-226)] + +**dialogs/mainDialog.js** -**dialogs/mainDialog.js** -[!code-javascript[hero cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=206-218)] +[!code-javascript[createOAuthCard](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=228-234)] -**dialogs/mainDialog.js** -[!code-javascript[sign in cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=259-265)] +### [Java](#tab/java) + +For examples of all the available cards, see the [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/06.using-cards) sample. + +**Cards.java** + +[!code-java[GetHeroCard](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/Cards.java?range=48-58)] + +**Cards.java** + +[!code-java[GetSigninCard](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/Cards.java?range=98-103)] ### [Python](#tab/python) -For examples of all the available cards, see the [Python cards sample](https://aka.ms/bot-cards-python-sample-code). +For examples of all the available cards, see the [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/06.using-cards) sample. **dialogs/main_dialog.py** -[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=163-179)] + +[!code-python[create_hero_card](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=167-183)] **dialogs/main_dialog.py** -[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=245-256)] + +[!code-python[create_oauth_card](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=185-197)] --- ## Send an Adaptive Card -Adaptive Card and MessageFactory are used to send rich messages including texts, images, video, audio and files to communicate with users. However, there are some differences between them. -First, only some channels support Adaptive Cards, and channels that do support it might partially support Adaptive Cards. For example, if you send an Adaptive Card in Facebook, the buttons won't work while texts and images work well. MessageFactory is just a helper class within the Bot Framework SDK to automate creation steps for you, and supported by most channels. +While you can use the _message factory_ to create a message that contains an attachment (of any sort), an _Adaptive Card_ is one specific type of attachment. Not all channels support Adaptive Cards, and some channels may only partially support Adaptive Cards. For example, if you send an Adaptive Card in Facebook, the buttons won't work while texts and images work well. The message factory is a Bot Framework SDK helper class used to automate creation steps for you. -Second, Adaptive Card delivers messages in the card format, and the channel determines the layout of the card. The format of messages MessageFactory delivers depends on the channel, and is not necessarily in the card format unless Adaptive Card is part of the attachment. +Adaptive Cards are an open card exchange format enabling developers to exchange UI content in a common and consistent way. However, not all channels support Adaptive Cards. -To find the latest information on Adaptive Card channel support, see the Adaptive Cards Designer. - -To use adaptive cards, be sure to add the `AdaptiveCards` NuGet package. +The [Adaptive Cards Designer](https://adaptivecards.io/designer/) provides a rich, interactive design-time experience for authoring adaptive cards. > [!NOTE] > You should test this feature with the channels your bot will use to determine whether those channels support adaptive cards. @@ -224,39 +286,49 @@ To use adaptive cards, be sure to add the `AdaptiveCards` NuGet package. To use Adaptive Cards, be sure to add the `AdaptiveCards` NuGet package. -The source code shown here is based on the [Using cards](https://aka.ms/bot-cards-sample-code) sample. +The following source code is from the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/06.using-cards) sample. + +**Cards.cs** -**Cards.cs** -[!code-csharp[adaptive cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=13-25)] +This example reads the Adaptive Card JSON from a file and adds it as an attachment. + +[!code-csharp[CreateAdaptiveCardAttachment](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Cards.cs?range=13-26&highlight=9-10)] ### [JavaScript](#tab/javascript) To use Adaptive Cards, be sure to add the `adaptivecards` npm package. -The source code shown here is based on the [JS Using Cards](https://aka.ms/bot-cards-js-sample-code) sample. +The following source code is from the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/06.using-cards) sample. + +**dialogs/mainDialog.js** -Here, the Adaptive card is stored in it's own file and included in our bot: +This example reads the Adaptive Card JSON from a file and creates a message activity with the card attached. -**resources/adaptiveCard.json** -[!code-json[adaptive cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/resources/adaptiveCard.json)] +[!code-javascript[import JSON file](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=6)] -The card is created as follows: +[!code-javascript[createAdaptiveCard](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=178-180)] -**dialogs/mainDialog.js** -[!code-javascript[adaptive cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=6)] -[!code-javascript[adaptive cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=170-172)] +### [Java](#tab/java) -### [Python](#tab/python) +The following source code is from the [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/06.using-cards) sample. -The source code shown here is based on the [Using cards](https://aka.ms/bot-cards-python-sample-code) sample. +**Cards.java** -**dialogs/resources/adaptive_card_example.py** -[!code-python[adaptive cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/resources/adaptive_card_example.py)] +This example reads the Adaptive Card JSON from a file and adds it as an attachment. -The card is created as follows: +[!code-java[CreateAdaptiveCardAttachment](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/Cards.java?range=30-46&highlight9-10)] + +### [Python](#tab/python) + +The following source code is from the [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/06.using-cards) sample. **bots/main_dialog.py** -[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=127-128)] + +This example reads the Adaptive Card JSON from a file and creates a message activity with the card attached. + +[!code-python[import JSON file](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=33)] + +[!code-python[create_adaptive_card](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=131-132)] --- @@ -266,71 +338,86 @@ Messages can also include multiple attachments in a carousel layout, which place ### [C#](#tab/csharp) -The source code shown here is based on the [Cards sample](https://aka.ms/bot-cards-sample-code). +The following source code is from the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/06.using-cards) sample. + +**Dialogs/MainDialog.cs** First, create the reply and define the attachments as a list. -**Dialogs/MainDialog.cs** -[!code-csharp[carousel of cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=61-66)] +[!code-csharp[ShowCardStepAsync excerpt](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=61-66)] -Then add the attachments. Here we're adding them one at a time, but feel free to manipulate the list to add the cards however you prefer. +Then add the attachments and set the layout type to _carousel_. +Here we're adding them one at a time, but feel free to manipulate the list to add the cards however you prefer. -**Dialogs/MainDialog.cs** -[!code-csharp[carousel of cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=104-113)] +[!code-csharp[ShowCardStepAsync excerpt](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=108-118)] Once the attachments are added, you can send the reply just like any other. -**Dialogs/MainDialog.cs** -[!code-csharp[carousel of cards](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=117-118)] +[!code-csharp[ShowCardStepAsync excerpt](~/../botbuilder-samples/samples/csharp_dotnetcore/06.using-cards/Dialogs/MainDialog.cs?range=122-123)] ### [JavaScript](#tab/javascript) -The source code shown here is based on the [JS cards sample](https://aka.ms/bot-cards-js-sample-code). +The following source code is from the [Using cards](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/06.using-cards) sample. + +**dialogs/mainDialog.js** + +Add the attachments and set the layout type to _carousel_. +Once the attachments are added, you can send the reply just like any other. -To send a carousel of cards, send a reply with the attachments as an array and the layout type defined as `Carousel`: +[!code-javascript[showCardStep excerpt](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=100-113)] -**dialogs/mainDialog.js** -[!code-javascript[carousel of cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=97-108)] +### [Java](#tab/java) -[!code-javascript[carousel of cards](~/../botbuilder-samples/samples/javascript_nodejs/06.using-cards/dialogs/mainDialog.js?range=113-116)] +The following source code is from the [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/06.using-cards) sample. -### [Python](#tab/python) +**MainDialog.java** -The source code shown here is based on [Python cards sample](https://aka.ms/bot-cards-python-sample-code). +First, create the reply and define the attachments as a list. -To send a carousel of cards, send a reply with the attachments as an array and the layout type defined as `Carousel`: +[!code-java[ShowCardStep excerpt](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/MainDialog.java?range=63-68)] -**dialogs/main_dialog.py** -[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=104-112)] +Then add the attachments and set the layout type to _carousel_. +Here we're adding them one at a time, but feel free to manipulate the list to add the cards however you prefer. -Once the attachments are added, you can send the reply. +[!code-java[ShowCardStep excerpt](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/MainDialog.java?range=109-119)] + +Once the attachments are added, you can send the reply just like any other. + +[!code-java[ShowCardStep excerpt](~/../botbuilder-samples/samples/java_springboot/06.using-cards/src/main/java/com/microsoft/bot/sample/usingcards/MainDialog.java?range=123-124)] + +### [Python](#tab/python) + +The source code shown here is based on [Using cards](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/06.using-cards) sample. **dialogs/main_dialog.py** -[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=114-115)] ---- +First, create the reply and define the attachments as a list. + +[!code-python[show_card_step excerpt](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=85)] - +Then add the attachments and set the layout type to _carousel_. +Here we're adding them one at a time, but feel free to manipulate the list to add the cards however you prefer. -## Additional resources +[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=107-116)] -See [design user experience](../bot-service-design-user-experience.md) for examples of available cards. +Once the attachments are added, you can send the reply just like any other. + +[!code-python[hero cards](~/../botbuilder-samples/samples/python/06.using-cards/dialogs/main_dialog.py?range=118-119)] -For detailed information on the schema, see the [Bot Framework card schema](https://aka.ms/botSpecs-cardSchema) and the [message activity section](https://aka.ms/botSpecs-activitySchema#message-activity) of the Bot Framework Activity schema. +--- ### Code sample for processing Adaptive Card input -This sample code shows one way to use Adaptive Card inputs within a bot dialog class. -It extends the current sample 06.using-cards by validating the input received in the text field from the responding client. -We first added text input and button functionality to the existing adaptive card by adding the following code just before the final bracket of adaptiveCard.json, found in the resources folder: +The following sample shows one way to use Adaptive Card inputs within a bot dialog class. +It extends the hero cards sample by validating the input received in the text field from the responding client. +You first need to add the text input and button functionality to the existing adaptive card by adding the following code just before the final bracket of **adaptiveCard.json**, located in the resources folder: ```json -... - "actions": [ - { - "type": "Action.ShowCard", - "title": "Text", - "card": { +"actions": [ + { + "type": "Action.ShowCard", + "title": "Text", + "card": { "type": "AdaptiveCard", "body": [ { @@ -349,14 +436,13 @@ We first added text input and button functionality to the existing adaptive card } } ] - ``` -Note that the input field is labeled "text" so our adaptive card will attach comment text data as Value.[text.] +The ID of the text input field is set to "text". When the user selects **OK**, the message the Adaptive Card generates will have a _value_ property that has a property named `text` that contains the information the user entered in the text input field of the card. ### [C#](#tab/csharp) -Our validator uses Newtonsoft.json to first convert this to a JObject, +Our validator uses **Newtonsoft.json** to first convert this to a `JObject`, and then create a trimmed text string for comparison. So add: ```csharp @@ -365,9 +451,13 @@ using System.Linq; using Newtonsoft.Json.Linq; ``` -to MainDialog.cs and install the latest stable nuget package of Newtonsoft.Json. -In the validator code we added the logic flow into the code comments. -This ChoiceValidator() code is placed into the 06.using-cards sample just after the closed brace public for declaration of MainDialog: + + + + +to **MainDialog.cs** and install the latest stable NuGet package of **Newtonsoft.Json**. +In the validator code, we added the logic flow into the code comments. +This `ChoiceValidator` method is placed into the **Using cards** sample just after the closed brace public for declaration of MainDialog: ```csharp private async Task ChoiceValidator( @@ -401,7 +491,7 @@ private async Task ChoiceValidator( } ``` -Now above in the MainDialog declaration change: +Now above in the `MainDialog` declaration change: ```csharp // Define the main dialog and its related components. @@ -415,16 +505,16 @@ to: AddDialog(new ChoicePrompt(nameof(ChoicePrompt), ChoiceValidator)); ``` -This will invoke your validator to look for Adaptive Card input each time a new ChoicePrompt is created. +This will invoke your validator to look for Adaptive Card input each time a new choice prompt is created. ### [JavaScript](#tab/javascript) -Open mainDialog.js and find the run method _async run(turnContext, accessor)_ +Open **mainDialog.js** and find the run method `async run(turnContext, accessor)` This method handles incoming activity. -Just after the call _dialogSet.add(this);_ add the following: +Just after the call `dialogSet.add(this);` add the following: ```JavaScript -// The following check looks for a non-existant text input +// The following check looks for a non-existent text input // plus Adaptive Card input in _activity.value.text // If both conditions exist, the Activity Card text // is copied into the text input field. @@ -435,16 +525,86 @@ if(turnContext._activity.text == null } ``` -If this check finds a non-existent text input from the client, it looks to see if there is input from an Adaptive Card. -If an Adaptive Card input exists at \_activity.value.text, it copies this into the normal text input field. +If this check finds a non-existent text input from the client, it looks to see if there's input from an Adaptive Card. +If an Adaptive Card input exists at `_activity.value.text`, it copies this into the normal text input field. + +### [Java](#tab/java) + +Our validator uses the **Serialization** helper from com.microsoft.bot.schema to first convert this to a `JsonNode`, +and then create a trimmed text string for comparison. We'll also need a few other imports to complete this, so add: + +```java +import com.fasterxml.jackson.databind.JsonNode; +import com.microsoft.bot.dialogs.prompts.PromptValidator; +import com.microsoft.bot.schema.Serialization; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +``` + + + + + +to **MainDialog.java**. +In the validator code, we added the logic flow into the code comments. +This `PromptValidator` expression is placed into the **Using cards** sample just after the closed brace public for declaration of MainDialog: + +```java +PromptValidator validator = (promptContext) -> { + // Retrieves Adaptive Card comment text as JObject. + // looks for JObject field "text" and converts that input into a trimmed text + // string. + JsonNode jsonNode = Serialization.getAs(promptContext.getContext().getActivity().getValue(), JsonNode.class); + JsonNode textNode = jsonNode != null ? jsonNode.get("text") : null; + String text = textNode != null ? textNode.textValue() : ""; + + // Logic: 1. if succeeded = true, just return promptContext + // 2. if false, see if JObject contained Adaptive Card input. + // No = (bad input) return promptContext + // Yes = update Value field with JObject text string, return "true". + if (!promptContext.getRecognized().getSucceeded() && text != null) { + Optional choice = promptContext.getOptions() + .getChoices() + .stream() + .filter(c -> StringUtils.compareIgnoreCase(c.getValue(), text) == 0) + .findFirst(); + + if (choice.isPresent()) { + promptContext.getRecognized().setValue(new FoundChoice() { + { + setValue(choice.get().getValue()); + } + }); + return CompletableFuture.completedFuture(true); + } + } + return CompletableFuture.completedFuture(promptContext.getRecognized().getSucceeded()); +}; +``` + +Now above in the `MainDialog` declaration change: + +```java +// Define the main dialog and its related components. +addDialog(new ChoicePrompt("ChoicePrompt")); +``` + +to: + +```java +// Define the main dialog and its related components. +addDialog(new ChoicePrompt("ChoicePrompt", validator, null)); +``` + +This will invoke your validator to look for Adaptive Card input each time a new choice prompt is created. ### [Python](#tab/python) -The source code shown here is based on the [Suggested actions](https://aka.ms/SuggestedActionsPython) sample. + -Create and sends an activity with suggested actions to the user. +Create and send an activity with suggested actions to the user. -This choice_validator() code is placed into the 06.using-cards sample just after the closed brace public for declaration of MainDialog: +This `choice_validator` method is placed into the **Using cards** sample just after the closed brace public for declaration of `MainDialog`: ```python @staticmethod @@ -465,7 +625,7 @@ async def choice_validator(prompt_context: PromptValidatorContext) -> bool: return prompt_context.recognized.succeeded ``` -Now above in the MainDialog declaration change: +Now above in the `MainDialog` declaration change: ```python self.add_dialog(ChoicePrompt(CARD_PROMPT)) @@ -477,16 +637,14 @@ to: self.add_dialog(ChoicePrompt(CARD_PROMPT, MainDialog.choice_validator)) ``` -This will invoke your validator to look for Adaptive Card input each time a new ChoicePrompt is created. +This will invoke your validator to look for Adaptive Card input each time a new choice prompt is created. --- -To test your code, once an Adaptive Card has been displayed, Click the "Text" button, Enter a valid selection such as "Hero Card" and click the "OK" button. - -![Test Adaptive Card](media/adaptive-card-input.png) +## Test the Using Cards bot -1. The first input will be used to start a new dialog. -2. Click the "OK" button again and this input will be used to select a new card. +1. Run the **Using cards** sample locally and open the bot in the Bot Framework Emulator. +1. Follow the prompts in the bot to display a card type, such as an Adaptive Card. ## Next steps diff --git a/articles/v4sdk/bot-builder-howto-add-suggested-actions.md b/articles/v4sdk/bot-builder-howto-add-suggested-actions.md index 6c3b8d4ba..184c39e5f 100644 --- a/articles/v4sdk/bot-builder-howto-add-suggested-actions.md +++ b/articles/v4sdk/bot-builder-howto-add-suggested-actions.md @@ -1,54 +1,59 @@ --- -title: Use button for input - Bot Service +title: Use button for input description: Learn how to send suggested actions within messages using the Bot Framework SDK for JavaScript. keywords: suggested actions, buttons, extra input -author: Kaiqb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 12/10/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Use button for input -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -You can enable your bot to present buttons that the user can tap to provide input. Buttons enhance user experience by enabling the user to answer a question or make a selection with a simple tap of a button, rather than having to type a response with a keyboard. Unlike buttons that appear within rich cards (which remain visible and accessible to the user even after being tapped), buttons that appear within the suggested actions pane will disappear after the user makes a selection. This prevents the user from tapping stale buttons within a conversation and simplifies bot development (since you will not need to account for that scenario). +Buttons enhance the conversational experience by letting the user answer a question or select the desired button, rather than having to type a response with a keyboard. Unlike buttons that appear within rich cards (which remain visible and accessible to the user even after being selected), buttons that appear within the suggested actions pane will disappear after the user makes a selection. This prevents the user from selecting stale buttons within a conversation and simplifies bot development since you won't need to account for that scenario. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Suggest action using button -*Suggested actions* enable your bot to present buttons. You can create a list of suggested actions (also known as "quick replies") that will be shown to the user for a single turn of the conversation: +*Suggested actions* enable your bot to present buttons. You can create a list of suggested actions (also known as _quick replies_) that will be shown to the user for a single turn of the conversation. # [C#](#tab/csharp) -The source code shown here is based on the [suggest actions sample](https://aka.ms/SuggestedActionsCSharp). +Here's an example from the [Suggested actions](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/08.suggested-actions) sample. -[!code-csharp[suggested actions](~/../botbuilder-samples/samples/csharp_dotnetcore/08.suggested-actions/Bots/SuggestedActionsBot.cs?range=87-101)] +[!code-csharp[suggested actions](~/../botbuilder-samples/samples/csharp_dotnetcore/08.suggested-actions/Bots/SuggestedActionsBot.cs?range=80-98)] # [JavaScript](#tab/javascript) -The source code shown here is based on the [suggested actions sample](https://aka.ms/SuggestActionsJS). +Here's an example from the [Suggested actions](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/08.suggested-actions) sample. -[!code-javascript[suggested actions](~/../botbuilder-samples/samples/javascript_nodejs/08.suggested-actions/bots/suggestedActionsBot.js?range=61-64)] +[!code-javascript[suggested actions](~/../botbuilder-samples/samples/javascript_nodejs/08.suggested-actions/bots/suggestedActionsBot.js?range=58-89)] +# [Java](#tab/java) -# [Python](#tab/python) +Here's an example from the [Suggested actions](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/08.suggested-actions) sample. -The source code shown here is based on the [suggested actions sample](https://aka.ms/SuggestActionsPython). +[!code-java[suggested actions](~/../botbuilder-samples/samples/java_springboot/08.suggested-actions/src/main/java/com/microsoft/bot/sample/suggestedactions/SuggestedActionsBot.java?range=102-136)] + +# [Python](#tab/python) -[!code-python[suggested actions](~/../botbuilder-samples/samples/python/08.suggested-actions/bots/suggested_actions_bot.py?range=63-81)] +Here's an example from the [Suggested actions](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/08.suggested-actions) sample. +[!code-python[suggested actions](~/../botbuilder-samples/samples/python/08.suggested-actions/bots/suggested_actions_bot.py?range=63-99)] --- ## Additional resources -You can access the complete source code shown here: -- [C# sample](https://aka.ms/SuggestedActionsCSharp) -- [JavaScript sample](https://aka.ms/SuggestActionsJS) -- [Python sample](https://aka.ms/SuggestActionsPython) +You can access the complete source code for the **Suggested actions** sample in [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/08.suggested-actions), [JavaScript](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/08.suggested-actions), [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/08.suggested-actions) and [Python](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/08.suggested-actions). ## Next steps diff --git a/articles/v4sdk/bot-builder-howto-expire-conversation.md b/articles/v4sdk/bot-builder-howto-expire-conversation.md new file mode 100644 index 000000000..bbd1647eb --- /dev/null +++ b/articles/v4sdk/bot-builder-howto-expire-conversation.md @@ -0,0 +1,754 @@ +--- +title: Expire a conversation +description: Learn how to expire a user's conversation with a bot. +keywords: expire, timeout +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Expire a conversation + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +A bot sometimes needs to restart a conversation from the beginning. For instance, if a user doesn't respond after a certain period of time. This article describes two methods for expiring a conversation: + +- Track the last time a message was received from a user, and clear state if the time is greater than a preconfigured length upon receiving the next message from the user. For more information, see the [user interaction expiration](#user-interaction-expiration) section. +- Use a storage layer feature, such as Cosmos DB Time To Live (TTL), to automatically clear state after a preconfigured length of time. For more information, see the [storage expiration](#storage-expiration) section. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + +## Prerequisites + +- If you don't have an Azure subscription, create a [free](https://azure.microsoft.com/free/) account before you begin. +- Knowledge of [bot basics][concept-basics], [managing state][concept-state], and the [dialogs library][concept-dialogs]. +- A copy of the **multi-turn prompt** sample in either [**C#**][cs-sample], [**JavaScript**][js-sample], [**Java**][java-sample], or [**Python**][python-sample]. + +## About this sample + +The sample code in this article begins with the structure of a multi-turn bot, and extends that bot's functionality by adding additional code (provided in the following sections). This extended code demonstrates how to clear conversation state after a certain time period has passed. + +## User Interaction Expiration + +This type of expiring conversation is accomplished by adding a _last accessed time_ property to the bot's conversation state. This property value is then compared to the current time within the _activity handler_ before processing activities. + +> [!NOTE] +> This example uses a 30 second timeout for ease of testing this pattern. + +# [C#](#tab/csharp) + +**appsettings.json** + +First, add an `ExpireAfterSeconds` setting to appsettings.json: + +```json +{ + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + "ExpireAfterSeconds": 30 +} +``` + +**Bots\DialogBot.cs** + +Next, add `ExpireAfterSeconds`, `LastAccessedTimeProperty`, and `DialogStateProperty` fields to the bot class and initialize them in the bot's constructor. Also add an `IConfiguration` parameter to the constructor with which to retrieve the `ExpireAfterSeconds` value. + +Instead of creating the dialog state property accessor inline in the `OnMessageActivityAsync` method, you're creating and recording it at initialization time. The bot will need the state property accessor not only to run the dialog, but also to clear the dialog state. + +```csharp +protected readonly int ExpireAfterSeconds; +protected readonly IStatePropertyAccessor LastAccessedTimeProperty; +protected readonly IStatePropertyAccessor DialogStateProperty; + +// Existing fields omitted... + +public DialogBot(IConfiguration configuration, ConversationState conversationState, UserState userState, T dialog, ILogger> logger) +{ + ConversationState = conversationState; + UserState = userState; + Dialog = dialog; + Logger = logger; + + ExpireAfterSeconds = configuration.GetValue("ExpireAfterSeconds"); + DialogStateProperty = ConversationState.CreateProperty(nameof(DialogState)); + LastAccessedTimeProperty = ConversationState.CreateProperty(nameof(LastAccessedTimeProperty)); +} +``` + +Finally, add code to the bot's `OnTurnAsync` method to clear the dialog state if the conversation is too old. + +```csharp +public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) +{ + // Retrieve the property value, and compare it to the current time. + var lastAccess = await LastAccessedTimeProperty.GetAsync(turnContext, () => DateTime.UtcNow, cancellationToken).ConfigureAwait(false); + if ((DateTime.UtcNow - lastAccess) >= TimeSpan.FromSeconds(ExpireAfterSeconds)) + { + // Notify the user that the conversation is being restarted. + await turnContext.SendActivityAsync("Welcome back! Let's start over from the beginning.").ConfigureAwait(false); + + // Clear state. + await ConversationState.ClearStateAsync(turnContext, cancellationToken).ConfigureAwait(false); + } + + await base.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); + + // Set LastAccessedTime to the current time. + await LastAccessedTimeProperty.SetAsync(turnContext, DateTime.UtcNow, cancellationToken).ConfigureAwait(false); + + // Save any state changes that might have occurred during the turn. + await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false); + await UserState.SaveChangesAsync(turnContext, false, cancellationToken).ConfigureAwait(false); +} +``` + +# [JavaScript](#tab/javascript) + +**.env** + +First, add an `ExpireAfterSeconds` setting to .env: + +```ini +MicrosoftAppId= +MicrosoftAppPassword= +ExpireAfterSeconds=30 +``` + +**bots\dialogBot.js** + +Next, add fields to `DialogBot` and update the constructor. Add local fields for `expireAfterSeconds` and `lastAccessedTimeProperty`. + +Add `expireAfterSeconds` as a parameter to the constructor and create the required `StatePropertyAccessor`: + +```javascript +constructor(expireAfterSeconds, conversationState, userState, dialog) { + + // Existing code omitted... + + this.lastAccessedTimeProperty = this.conversationState.createProperty('LastAccessedTime'); + this.expireAfterSeconds = expireAfterSeconds; + + // Existing code omitted... +} +``` + +Add code to the bot's `run` method: + +```javascript +async run(context) { + // Retrieve the property value, and compare it to the current time. + const now = new Date(); + const lastAccess = new Date(await this.lastAccessedTimeProperty.get(context, now.toISOString())); + if (now !== lastAccess && ((now.getTime() - lastAccess.getTime()) / 1000) >= this.expireAfterSeconds) { + // Notify the user that the conversation is being restarted. + await context.sendActivity("Welcome back! Let's start over from the beginning."); + + // Clear state. + await this.conversationState.clear(context); + } + + await super.run(context); + + // Set LastAccessedTime to the current time. + await this.lastAccessedTimeProperty.set(context, now.toISOString()); + + // 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); +} +``` + +**index.js** + +Lastly, update `index.js` to send the `ExpireAfterSeconds` parameter to `DialogBot`: + +```javascript +const bot = new DialogBot(process.env.ExpireAfterSeconds, conversationState, userState, dialog); +``` + +# [Java](#tab/java) + +**application.properties** + +First, add an `ExpireAfterSeconds` setting to application.properties: + +```ini +MicrosoftAppId= +MicrosoftAppPassword= +server.port=3978 +ExpireAfterSeconds=30 +``` + +**DialogBot.java** + +Next, add `expireAfterSeconds`, `lastAccessedTimeProperty`, and `dialogStateProperty` fields to the bot class and initialize them in the bot's constructor. Also add a `Configuration` parameter to the constructor to retrieve the `ExpireAfterSeconds` value. + +Instead of creating the dialog state property accessor inline in the `onMessageActivity` method, you create and record it at initialization time. The bot will need the state property accessor not only to run the dialog, but also to clear the dialog state. + +```java + protected final int expireAfterSeconds; + protected final StatePropertyAccessor lastAccessedTimeProperty; + protected final StatePropertyAccessor dialogStateProperty; + +// Existing fields omitted... + + public DialogBot( + Configuration configuration, + ConversationState withConversationState, + UserState withUserState, + Dialog withDialog + ) { + dialog = withDialog; + conversationState = withConversationState; + userState = withUserState; + + expireAfterSeconds = configuration.getProperty("ExpireAfterSeconds") != null ? + Integer.parseInt(configuration.getProperty("ExpireAfterSeconds")) : + 30; + lastAccessedTimeProperty = conversationState.createProperty("LastAccessedTimeProperty"); + dialogStateProperty = conversationState.createProperty("DialogStateProperty"); + + } +``` + +Finally, add code to the bot's `onTurn` method to clear the dialog state if the conversation is too old. + +```java + @Override + public CompletableFuture onTurn( + TurnContext turnContext + ) { + LocalTime lastAccess = lastAccessedTimeProperty.get(turnContext).join(); + if (lastAccess != null + && (java.time.temporal.ChronoUnit.SECONDS.between(lastAccess, LocalTime.now()) >= expireAfterSeconds)) { + turnContext.sendActivity("Welcome back! Let's start over from the beginning.").join(); + conversationState.clearState(turnContext).join(); + } + return lastAccessedTimeProperty.set(turnContext, LocalTime.now()).thenCompose(setResult -> { + return super.onTurn(turnContext) + .thenCompose(result -> conversationState.saveChanges(turnContext)) + // Save any state changes that might have occurred during the turn. + .thenCompose(result -> userState.saveChanges(turnContext)); + }); + } +``` + +## [Python](#tab/python) + +**config.py** + +First, add an `ExpireAfterSeconds` setting to config.py: + +```python +PORT = 3978 +APP_ID = os.environ.get("MicrosoftAppId", "") +APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "") +EXPIRE_AFTER_SECONDS = os.environ.get("ExpireAfterSeconds", 30) +``` + +**bots\dialog_bot.py** + +Next, add fields to `DialogBot` and update the constructor. Add local fields for `expire_after_seconds` and `last_accessed_time_property`. + +Add `expire_after_seconds` as a parameter to the constructor and create the required `StatePropertyAccessor`: + +```python +def __init__( + self, + expire_after_seconds: int, + conversation_state: ConversationState, + user_state: UserState, + dialog: Dialog, +): + # Existing code omitted... + + self.expire_after_seconds = expire_after_seconds + self.dialog_state_property = conversation_state.create_property("DialogState") + self.last_accessed_time_property = conversation_state.create_property("LastAccessedTime") + self.conversation_state = conversation_state + self.user_state = user_state + self.dialog = dialog +``` + +Change `on_message_activity` so it uses the `dialog_state_property`: + +```python +async def on_message_activity(self, turn_context: TurnContext): + await DialogHelper.run_dialog( + self.dialog, + turn_context, + self.dialog_state_property, + ) +``` + +Add code to the bot's `on_turn` method: + +```python +async def on_turn(self, turn_context: TurnContext): + # Retrieve the property value, and compare it to the current time. + now_seconds = int(time.time()) + last_access = int( + await self.last_accessed_time_property.get(turn_context, now_seconds) + ) + if now_seconds != last_access and ( + now_seconds - last_access >= self.expire_after_seconds + ): + # Notify the user that the conversation is being restarted. + await turn_context.send_activity( + "Welcome back! Let's start over from the beginning." + ) + + # Clear state. + await self.conversation_state.clear_state(turn_context) + await self.conversation_state.save_changes(turn_context, True) + + await super().on_turn(turn_context) + + # Set LastAccessedTime to the current time. + await self.last_accessed_time_property.set(turn_context, now_seconds) + + # Save any state changes that might have occurred during the turn. + await self.conversation_state.save_changes(turn_context) + await self.user_state.save_changes(turn_context) +``` + +**app.py** + +Lastly, update `app.py` to send the `EXPIRE_AFTER_SECONDS` parameter to `DialogBot`: + +```python +BOT = DialogBot(CONFIG.EXPIRE_AFTER_SECONDS, CONVERSATION_STATE, USER_STATE, DIALOG) +``` + +--- + +## Storage Expiration + +Cosmos DB provides a Time To Live (TTL) feature that allows you to delete items automatically from a container after a certain time period. This can be configured from within the Azure portal or during container creation (using the language-specific Cosmos DB SDKs). + +The Bot Framework SDK doesn't expose a TTL configuration setting. However, container initialization can be overridden and the Cosmos DB SDK can be used to configure TTL prior to Bot Framework storage initialization. + +# [C#](#tab/csharp) + +Start with a fresh copy of the **multi-turn prompt** sample, and add the `Microsoft.Bot.Builder.Azure` NuGet package to the project. + +**appsettings.json** + +Update appsettings.json to include Cosmos DB storage options: + +```json +{ + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + + "CosmosDbTimeToLive": 30, + "CosmosDbEndpoint": "", + "CosmosDbAuthKey": "", + "CosmosDbDatabaseId": "", + "CosmosDbUserStateContainerId": "", + "CosmosDbConversationStateContainerId": "" +} +``` + +Notice the two ContainerIds, one for `UserState` and one for `ConversationState`. The default TTL is set on the `ConversationState` container, but not on `UserState`. + +**CosmosDbStorageInitializerHostedService.cs** + +Next, create a `CosmosDbStorageInitializerHostedService` class, which will create the container with the configured Time To Live. + +```csharp +// Add required using statements... + +public class CosmosDbStorageInitializerHostedService : IHostedService +{ + readonly CosmosDbPartitionedStorageOptions _storageOptions; + readonly int _cosmosDbTimeToLive; + + public CosmosDbStorageInitializerHostedService(IConfiguration config) + { + _storageOptions = new CosmosDbPartitionedStorageOptions() + { + CosmosDbEndpoint = config["CosmosDbEndpoint"], + AuthKey = config["CosmosDbAuthKey"], + DatabaseId = config["CosmosDbDatabaseId"], + ContainerId = config["CosmosDbConversationStateContainerId"] + }; + + _cosmosDbTimeToLive = config.GetValue("CosmosDbTimeToLive"); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + using (var client = new CosmosClient( + _storageOptions.CosmosDbEndpoint, + _storageOptions.AuthKey, + _storageOptions.CosmosClientOptions ?? new CosmosClientOptions())) + { + // Create the contaier with the provided TTL + var containerResponse = await client + .GetDatabase(_storageOptions.DatabaseId) + .DefineContainer(_storageOptions.ContainerId, "/id") + .WithDefaultTimeToLive(_cosmosDbTimeToLive) + .WithIndexingPolicy().WithAutomaticIndexing(false).Attach() + .CreateIfNotExistsAsync(_storageOptions.ContainerThroughput) + .ConfigureAwait(false); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} +``` + +**Startup.cs** + +Lastly, update `Startup.cs` to use the storage initializer, and Cosmos DB for state: + +```csharp +// Existing code omitted... + +// commented out MemoryStorage, since we are using CosmosDbPartitionedStorage instead +// services.AddSingleton(); + +// Add the Initializer as a HostedService (so it's called during the app service startup) +services.AddHostedService(); + +// Create the storage options for User state +var userStorageOptions = new CosmosDbPartitionedStorageOptions() +{ + CosmosDbEndpoint = Configuration["CosmosDbEndpoint"], + AuthKey = Configuration["CosmosDbAuthKey"], + DatabaseId = Configuration["CosmosDbDatabaseId"], + ContainerId = Configuration["CosmosDbUserStateContainerId"] +}; + +// Create the User state. (Used in this bot's Dialog implementation.) +services.AddSingleton(new UserState(new CosmosDbPartitionedStorage(userStorageOptions))); + +// Create the storage options for Conversation state +var conversationStorageOptions = new CosmosDbPartitionedStorageOptions() +{ + CosmosDbEndpoint = Configuration["CosmosDbEndpoint"], + AuthKey = Configuration["CosmosDbAuthKey"], + DatabaseId = Configuration["CosmosDbDatabaseId"], + ContainerId = Configuration["CosmosDbConversationStateContainerId"] +}; + +// Create the Conversation state. (Used by the Dialog system itself.) +services.AddSingleton(new ConversationState(new CosmosDbPartitionedStorage(conversationStorageOptions))); + +// Existing code omitted... +``` + +# [JavaScript](#tab/javascript) + +Start with a fresh copy of the **multi-turn prompt** sample. + +**.env** + +Update .env to include Cosmos DB storage options: + +```ini +MicrosoftAppId= +MicrosoftAppPassword= + +CosmosDbTimeToLive=30 +CosmosDbEndpoint= +CosmosDbAuthKey= +CosmosDbDatabaseId= +CosmosDbUserStateContainerId= +CosmosDbConversationStateContainerId= +``` + +Notice the two ContainerIds, one for `UserState` and one for `ConversationState`. The default TTL is set on the `ConversationState` container, but not `UserState`. + +**project.json** + +Next, add the `botbuilder-azure` npm package to project.json. + +```json +"dependencies": { + "botbuilder": "~4.9.2", + "botbuilder-dialogs": "~4.9.2", + "botbuilder-azure": "~4.9.2", + "dotenv": "^8.2.0", + "path": "^0.12.7", + "restify": "~8.5.1" +}, +``` + +**index.js** + +Add the necessary require statements to index.js: + +```javascript +const { CosmosDbPartitionedStorage } = require('botbuilder-azure'); +const { CosmosClient } = require('@azure/cosmos'); +``` + +Replace `MemoryStorage`, `ConversationState` and `UserState` creation with: + +```javascript +// const memoryStorage = new MemoryStorage(); + +// Storage options for Conversation State +const conversationStorageOptions = { + cosmosDbEndpoint: process.env.CosmosDbEndpoint, + authKey: process.env.CosmosDbAuthKey, + databaseId: process.env.CosmosDbDatabaseId, + containerId: process.env.CosmosDbConversationStateContainerId +}; + +// Create a cosmosClient, and set defaultTtl (with other properties) +var cosmosClient = new CosmosClient({ + endpoint: conversationStorageOptions.cosmosDbEndpoint, + key: conversationStorageOptions.authKey, + ...conversationStorageOptions.cosmosClientOptions, +}); + +// Create the container with the provided TTL. +Promise.resolve(cosmosClient + .database(conversationStorageOptions.databaseId) + .containers.createIfNotExists({ + id: conversationStorageOptions.containerId, + partitionKey: { + paths: ['/id'] + }, + defaultTtl: parseInt(process.env.CosmosDbTimeToLive, 10) + })); + +// Storage options for User State +const userStorageOptions = { + cosmosDbEndpoint: process.env.CosmosDbEndpoint, + authKey: process.env.CosmosDbAuthKey, + databaseId: process.env.CosmosDbDatabaseId, + containerId: process.env.CosmosDbUserStateContainerId +}; + +// Create state instances. +const conversationState = new ConversationState(new CosmosDbPartitionedStorage(conversationStorageOptions)); +const userState = new UserState(new CosmosDbPartitionedStorage(userStorageOptions)); +``` + +Finally, run npm install before starting your bot. + +```cmd +npm install +``` + +# [Java](#tab/java) + +Start with a fresh copy of the **multi-turn prompt** sample, and add the following dependencies to the pom.xml file: + +```xml + + com.microsoft.bot + bot-azure + 4.13.0 + + + com.azure + azure-cosmos + +``` + +**application.properties** + +Update application.properties to include Cosmos DB storage options: + +```ini +MicrosoftAppId= +MicrosoftAppPassword= +server.port=3978 + +CosmosDbTimeToLive = 30 +CosmosDbEndpoint = +CosmosDbAuthKey = +CosmosDbDatabaseId = +CosmosDbUserStateContainerId = +CosmosDbConversationStateContainerId = +``` + +Notice the two ContainerIds, one for `UserState` and one for `ConversationState`. The default TTL is set on the `ConversationState` container, but not on `UserState`. + +**CosmosDbStorageInitializer.java** + +Next, create a `CosmosDbStorageInitializer` class, which will create the container with the configured Time To Live. + +```java +package com.microsoft.bot.sample.multiturnprompt; + +import com.azure.cosmos.CosmosAsyncClient; +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.models.CosmosContainerProperties; +import com.microsoft.bot.azure.CosmosDbPartitionedStorageOptions; +import com.microsoft.bot.integration.Configuration; + +public class CosmosDbStorageInitializer { + + final CosmosDbPartitionedStorageOptions storageOptions; + final int cosmosDbTimeToLive; + + public CosmosDbStorageInitializer(Configuration configuration) { + storageOptions = new CosmosDbPartitionedStorageOptions(); + storageOptions.setCosmosDbEndpoint(configuration.getProperty("CosmosDbEndpoint")); + storageOptions.setAuthKey(configuration.getProperty("CosmosDbAuthKey")); + storageOptions.setDatabaseId(configuration.getProperty("CosmosDbDatabaseId")); + storageOptions.setContainerId(configuration.getProperty("CosmosDbConversationStateContainerId")); + cosmosDbTimeToLive = configuration.getProperty("CosmosDbTimeToLive") != null + ? Integer.parseInt(configuration.getProperty("CosmosDbTimeToLive")) + : 30; + } + + public void initialize() { + + CosmosAsyncClient client = new CosmosClientBuilder().endpoint(storageOptions.getCosmosDbEndpoint()) + .key(storageOptions.getAuthKey()).buildAsyncClient(); + + client.createDatabaseIfNotExists(storageOptions.getDatabaseId()).block(); + CosmosContainerProperties cosmosContainerProperties = new CosmosContainerProperties( + storageOptions.getContainerId(), "/id"); + cosmosContainerProperties.setDefaultTimeToLiveInSeconds(cosmosDbTimeToLive); + client.getDatabase(storageOptions.getDatabaseId()).createContainerIfNotExists(cosmosContainerProperties) + .block(); + client.close(); + } +} + +``` + +**Application.java** + +Lastly, update `Application.java` to use the storage initializer, and Cosmos DB for state: + +```java +// Existing code omitted... + +@Override +public ConversationState getConversationState(Storage storage) { + Configuration configuration = getConfiguration(); + CosmosDbStorageInitializer initializer = new CosmosDbStorageInitializer(configuration); + initializer.initialize(); + + CosmosDbPartitionedStorageOptions storageOptions = new CosmosDbPartitionedStorageOptions(); + storageOptions.setCosmosDbEndpoint(configuration.getProperty("CosmosDbEndpoint")); + storageOptions.setAuthKey(configuration.getProperty("CosmosDbAuthKey")); + storageOptions.setDatabaseId(configuration.getProperty("CosmosDbDatabaseId")); + storageOptions.setContainerId(configuration.getProperty("CosmosDbConversationStateContainerId")); + return new ConversationState(new CosmosDbPartitionedStorage(storageOptions)); +} + +/** + * Returns a UserState object. Default scope of Singleton. + * + * @param storage The Storage object to use. + * @return A UserState object. + */ +@Override +public UserState getUserState(Storage storage) { + Configuration configuration = getConfiguration(); + CosmosDbPartitionedStorageOptions storageOptions = new CosmosDbPartitionedStorageOptions(); + storageOptions.setCosmosDbEndpoint(configuration.getProperty("CosmosDbEndpoint")); + storageOptions.setAuthKey(configuration.getProperty("CosmosDbAuthKey")); + storageOptions.setDatabaseId(configuration.getProperty("CosmosDbDatabaseId")); + storageOptions.setContainerId(configuration.getProperty("CosmosDbUserStateContainerId")); + return new UserState(new CosmosDbPartitionedStorage(storageOptions)); +} +``` + +## [Python](#tab/python) + +Start with a fresh copy of the **multi-turn prompt** sample. + +**config.py** + +Update `config.py` to include Cosmos DB storage options: + +```python +PORT = 3978 +APP_ID = os.environ.get("MicrosoftAppId", "") +APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "") + +COSMOSDB_TTL = os.environ.get("CosmosDbTimeToLive", 30) +COSMOSDB_ENDPOINT = os.environ.get("CosmosDbEndpoint", "") +COSMOSDB_AUTH_KEY = os.environ.get("CosmosDbAuthKey", "") +COSMOSDB_DATABASE_ID = os.environ.get("CosmosDbDatabaseId", "") +COSMOSDB_USER_STATE_CONTAINER_ID = os.environ.get("CosmosDbUserStateContainerId", "") +COSMOSDB_CONVERSATION_STATE_CONTAINER_ID = os.environ.get("CosmosDbConversationStateContainerId", "") +``` + +Notice the two ContainerIds, one for `UserState` and one for `ConversationState`. The default TTL is set on the `ConversationState` container, but not `UserState`. + +**requirements.txt** + +Next, add the `botbuilder-azure` package to requirements.txt. + +```text +botbuilder-integration-aiohttp>=4.10.0 +botbuilder-dialogs>=4.10.0 +botbuilder-ai>=4.10.0 +botbuilder-azure>=4.10.0 +``` + +Then, run pip install: + +```cmd +pip install -r requirements.txt +``` + +**app.py** + +```python +client = cosmos_client.CosmosClient( + CONFIG.COSMOSDB_ENDPOINT, {"masterKey": CONFIG.COSMOSDB_AUTH_KEY}, +) + +containers = list(client.QueryContainers("dbs/" + CONFIG.COSMOSDB_DATABASE_ID, { + "query": "SELECT * FROM r WHERE r.id=@id", + "parameters": [ + {"name": "@id", "value": CONFIG.COSMOSDB_CONVERSATION_STATE_CONTAINER_ID} + ], + })) + +if len(containers) < 1: + new_container = client.CreateContainer( + "dbs/" + CONFIG.COSMOSDB_DATABASE_ID, + { + "defaultTtl": CONFIG.COSMOSDB_TTL, + "id": CONFIG.COSMOSDB_CONVERSATION_STATE_CONTAINER_ID, + "partitionKey": {"paths": ["/id"], "kind": "Hash",}, + }, + ) +``` + +--- + +Cosmos DB will now automatically delete conversation state records after 30 seconds of inactivity. + +For more information, see [Configure time to live in Azure Cosmos DB][cosmos-ttl] + +## To test the bot + +1. If you haven't done so already, install the [Bot Framework Emulator][emulator-readme]. +1. Run the sample locally on your machine. +1. Start the Emulator, connect to your bot, and send a message to it. +1. After one of the prompts, wait 30 seconds before responding. + + + +[concept-basics]: bot-builder-basics.md +[concept-state]: bot-builder-concept-state.md +[concept-dialogs]: bot-builder-concept-dialog.md + +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/05.multi-turn-prompt +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/05.multi-turn-prompt +[python-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/05.multi-turn-prompt + +[cosmos-ttl]: /azure/cosmos-db/how-to-time-to-live +[emulator-readme]: https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md diff --git a/articles/v4sdk/bot-builder-howto-handle-user-interrupt.md b/articles/v4sdk/bot-builder-howto-handle-user-interrupt.md index 63ffdf1cd..0a989d1b2 100644 --- a/articles/v4sdk/bot-builder-howto-handle-user-interrupt.md +++ b/articles/v4sdk/bot-builder-howto-handle-user-interrupt.md @@ -1,37 +1,46 @@ --- -title: Handle user interruptions - Bot Service -description: Learn how to handle user interrupt and direct conversation flow. +title: Handle user interruptions +description: Learn how bots handle user interruptions. See how to implement help and cancel interruptions, how to create and test bots, and how to handle unexpected errors. keywords: interrupt, interruptions, switching topic, break -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/24/2020 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Handle user interruptions -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Handling interruptions is an important aspect of a robust bot. Users will not always follow your defined conversation flow, step by step. They may try to ask a question in the middle of the process, or simply want to cancel it instead of completing it. In this topic, we will explore some common ways to handle user interruptions in your bot. +Handling interruptions is an important aspect of a robust bot. Users won't always follow your defined conversation flow, step by step. They may try to ask a question in the middle of the process, or simply want to cancel it instead of completing it. This article describes some common ways to handle user interruptions in your bot. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Knowledge of [bot basics][concept-basics], [managing state][concept-state], the [dialogs library][concept-dialogs], and how to [reuse dialogs][component-dialogs]. -- A copy of the core bot sample in either [**CSharp**][cs-sample], [**JavaScript**][js-sample] or [**Python**][python-sample]. +- A copy of the core bot sample in [C#][cs-sample], [JavaScript][js-sample], [Java][java-sample] or [Python][python-sample]. + +The core bot sample uses Language Understanding (LUIS) to identify user intents; however, identifying user intent isn't the focus of this article. +For information about identifying user intents, see [Natural language understanding](bot-builder-concept-luis.md) and [Add natural language understanding to your bot](bot-builder-howto-v4-luis.md). + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] ## About this sample -The sample used in this article models a flight booking bot that uses dialogs to get flight information from the user. At any time during the conversation with the bot, the user can issue _help_ or _cancel_ commands to cause an interruption. There are two types of interruptions we handle here: +The sample used in this article models a flight booking bot that uses dialogs to get flight information from the user. At any time during the conversation with the bot, the user can issue _help_ or _cancel_ commands to cause an interruption. There are two types of interruptions handled: -- **Turn level**: Bypass processing at the turn level but leave the dialog on the stack with the information that was provided. In the next turn, continue from where we left off. +- **Turn level**: Bypass processing at the turn level but leave the dialog on the stack with the information that was provided. In the next turn, continue from where the conversation left off. - **Dialog level**: Cancel the processing completely, so the bot can start all over again. ## Define and implement the interruption logic -First, we define and implement the _help_ and _cancel_ interruptions. +First, define and implement the _help_ and _cancel_ interruptions. # [C#](#tab/csharp) @@ -39,17 +48,17 @@ To use dialogs, install the **Microsoft.Bot.Builder.Dialogs** NuGet package. **Dialogs\CancelAndHelpDialog.cs** -We begin by implementing the `CancelAndHelpDialog` class to handle user interruptions. +Implement the `CancelAndHelpDialog` class to handle user interruptions. The cancelable dialogs, `BookingDialog` and `DateResolverDialog` derive from this class. [!code-csharp[Class signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/CancelAndHelpDialog.cs?range=12)] -In the `CancelAndHelpDialog` class the `OnContinueDialogAsync` method calls the `InerruptAsync` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `InterruptAsync` is returned. +In the `CancelAndHelpDialog` class, the `OnContinueDialogAsync` method calls the `InterruptAsync` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `InterruptAsync` is returned. [!code-csharp[Overrides](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/CancelAndHelpDialog.cs?range=22-31)] -If the user types "help", the `InterrupAsync` method sends a message and then calls `DialogTurnResult (DialogTurnStatus.Waiting)` to indicate that the dialog on top is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and in the next turn we continue from where we left off. +If the user types "help", the `InterruptAsync` method sends a message and then calls `DialogTurnResult (DialogTurnStatus.Waiting)` to indicate that the dialog on top is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and the next turn continues from where the conversation left off. -If the user types "cancel", it calls `CancelAllDialogsAsync` on its inner dialog context, which clears its dialog stack and causes it to exit with a cancelled status and no result value. To the `MainDialog` (shown later on), it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. +If the user types "cancel", it calls `CancelAllDialogsAsync` on its inner dialog context, which clears its dialog stack and causes it to exit with a canceled status and no result value. To the `MainDialog` (shown later on), it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. [!code-csharp[Interrupt](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/CancelAndHelpDialog.cs?range=33-56)] @@ -59,41 +68,59 @@ To use dialogs, install the **botbuilder-dialogs** npm package. **dialogs/cancelAndHelpDialog.js** -We begin by implementing the `CancelAndHelpDialog` class to handle user interruptions. +Implement the `CancelAndHelpDialog` class to handle user interruptions. The cancelable dialogs, `BookingDialog` and `DateResolverDialog` extend this class. [!code-javascript[Class signature](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/cancelAndHelpDialog.js?range=11)] -In the `CancelAndHelpDialog` class the `onContinueDialog` method calls the `interrupt` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `interrupt` is returned. +In the `CancelAndHelpDialog` class, the `onContinueDialog` method calls the `interrupt` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `interrupt` is returned. [!code-javascript[Overrides](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/cancelAndHelpDialog.js?range=12-18)] -If the user types "help", the `interrupt` method sends a message and then returns a `{ status: DialogTurnStatus.waiting }` object to indicate that the dialog on top is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and in the next turn we continue from where we left off. +If the user types "help", the `interrupt` method sends a message and then returns a `{ status: DialogTurnStatus.waiting }` object to indicate that the dialog on top is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and the next turn continues from where the conversation left off. -If the user types "cancel", it calls `cancelAllDialogs` on its inner dialog context, which clears its dialog stack and causes it to exit with a cancelled status and no result value. To the `MainDialog` (shown later on), it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. +If the user types "cancel", it calls `cancelAllDialogs` on its inner dialog context, which clears its dialog stack and causes it to exit with a canceled status and no result value. To the `MainDialog` (shown later on), it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. [!code-javascript[Interrupt](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/cancelAndHelpDialog.js?range=20-39)] +# [Java](#tab/java) + +**CancelAndHelpDialog.java** + +Implement the `CancelAndHelpDialog` class to handle user interruptions. The cancelable dialogs, `BookingDialog` and `DateResolverDialog` derive from this class. + +[!code-java[Class signature](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/CancelAndHelpDialog.java?range=20)] + +In the `CancelAndHelpDialog` class, the `onContinueDialog` method calls the `interrupt` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `interrupt` is returned. + +[!code-java[Overrides](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/CancelAndHelpDialog.java?range=43-51)] + +If the user types "help", the `interrupt` method sends a message and then calls `DialogTurnResult(DialogTurnStatus.WAITING)` to indicate that the dialog on top is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and the next turn continues from where the conversation left off. + +If the user types "cancel", it calls `cancelAllDialogs` on its inner dialog context, which clears its dialog stack and causes it to exit with a canceled status and no result value. To the `MainDialog` (shown later on), it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. + +[!code-java[Interrupt](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/CancelAndHelpDialog.java?range=53-79)] + ## [Python](#tab/python) To use dialogs, install the `botbuilder-dialogs` package and make sure that the sample `requirements.txt` file contains the proper reference such as `botbuilder-dialogs>=4.5.0`. For more information, about installing the packages, see the samples repository [README](https://github.com/microsoft/botbuilder-python) file. + > [!NOTE] -> Doing `pip install botbuilder-dialogs` will also install `botbuilder-core`, `botbulder-connector`, and `botbuilder-schema`. +> Running `pip install botbuilder-dialogs` will also install `botbuilder-core`, `botbuilder-connector`, and `botbuilder-schema`. **dialogs/cancel-and-help-dialog.py** -We begin by implementing the `CancelAndHelpDialog` class to handle user interruptions. +Implement the `CancelAndHelpDialog` class to handle user interruptions. The cancelable dialogs, `BookingDialog` and `DateResolverDialog` derive from this class. [!code-python[class signature](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/cancel_and_help_dialog.py?range=14)] -In the `CancelAndHelpDialog` class the `on_continue_dialog` method calls the `interrupt` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `InterruptAsync` is returned. +In the `CancelAndHelpDialog` class, the `on_continue_dialog` method calls the `interrupt` method to check if the user has interrupted the normal flow. If the flow is interrupted, base class methods are called; otherwise, the return value from the `interrupt` is returned. [!code-python[dialog](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/cancel_and_help_dialog.py?range=18-23)] -If the user types *help* or *?*, the `interrupt` method sends a message and then calls -`DialogTurnResult(DialogTurnStatus.Waiting)` to indicate that the dialog on top of the stack is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and in the next turn we continue from where we left off. +If the user types "help" or "?", the `interrupt` method sends a message and then calls `DialogTurnResult(DialogTurnStatus.Waiting)` to indicate that the dialog on top of the stack is waiting for a response from the user. In this way, the conversation flow is interrupted for a turn only, and the next turn continues from where the conversation left off. -If the user types *cancel* or *quit*, it calls `cancel_all_dialogs()` on its inner dialog context, which clears its dialog stack and causes it to exit with a cancelled status and no result value. To the `MainDialog`, shown later, it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. +If the user types "cancel" or "quit", it calls `cancel_all_dialogs()` on its inner dialog context, which clears its dialog stack and causes it to exit with a canceled status and no result value. To the `MainDialog`, shown later, it will appear that the booking dialog ended and returned null, similar to when the user chooses not to confirm their booking. [!code-python[interrupt](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/cancel_and_help_dialog.py?range=25-47)] @@ -101,7 +128,7 @@ If the user types *cancel* or *quit*, it calls `cancel_all_dialogs()` on its inn ## Check for interruptions each turn -Now that we've covered how the interrupt handling class works, let's step back and look at what happens when the bot receives a new message from the user. +Once the interrupt handling class is implemented, review what happens when this bot receives a new message from the user. # [C#](#tab/csharp) @@ -109,13 +136,13 @@ Now that we've covered how the interrupt handling class works, let's step back a As the new message activity arrives, the bot runs the `MainDialog`. The `MainDialog` prompts the user for what it can help with. And then it starts the `BookingDialog` in the `MainDialog.ActStepAsync` method, with a call to `BeginDialogAsync` as shown below. -[!code-csharp[ActStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=58-101&highlight=6,26)] +[!code-csharp[ActStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=59-102&highlight=6,26)] -Next, in the `FinalStepAsync` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or cancelled. +Next, in the `FinalStepAsync` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or canceled. -[!code-csharp[FinalStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=130-150)] +[!code-csharp[FinalStepAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=131-151)] -The code in `BookingDialog` is not shown here as it is not directly related to interruption handling. It is used to prompt users for booking details. You can find that code in **Dialogs\BookingDialogs.cs**. +The code in `BookingDialog` isn't shown here as it's not directly related to interruption handling. It's used to prompt users for booking details. You can find that code in **Dialogs\BookingDialogs.cs**. # [JavaScript](#tab/javascript) @@ -123,23 +150,37 @@ The code in `BookingDialog` is not shown here as it is not directly related to i As the new message activity arrives, the bot runs the `MainDialog`. The `MainDialog` prompts the user for what it can help with. And then it starts the `bookingDialog` in the `MainDialog.actStep` method, with a call to `beginDialog` as shown below. -[!code-javascript[Act step](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=71-115&highlight=6,27)] +[!code-javascript[Act step](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=73-117&highlight=6,27)] + +Next, in the `finalStep` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or canceled. + +[!code-javascript[Final step](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=144-161)] + +The code in `BookingDialog` isn't shown here as it's not directly related to interruption handling. It's used to prompt users for booking details. You can find that code in **dialogs/bookingDialogs.js**. + +# [Java](#tab/java) + +**MainDialog.java** + +As the new message activity arrives, the bot runs the `MainDialog`. The `MainDialog` prompts the user for what it can help with. And then, it starts the `BookingDialog` in the `MainDialog.actStep` method, with a call to `beginDialog` as shown below. -Next, in the `finalStep` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or cancelled. +[!code-java[ActStep](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/MainDialog.java?range=100-156&highlight=4,27)] -[!code-javascript[Final step](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=142-159)] +Next, in the `finalStep` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or canceled. -The code in `BookingDialog` is not shown here as it is not directly related to interruption handling. It is used to prompt users for booking details. You can find that code in **dialogs/bookingDialogs.js**. +[!code-java[FinalStep](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/MainDialog.java?range=207-231)] + +The code in `BookingDialog` isn't shown here as it's not directly related to interruption handling. It's used to prompt users for booking details. You can find that code in **BookingDialogs.java**. ## [Python](#tab/python) **dialogs/main_dialog.py** -As the new message activity arrives, the bot runs the `MainDialog`. The `MainDialog` prompts the user for what it can help with. And then it starts the `bookingDialog` in the `MainDialog.act_step` method, with a call to `begin_dialog` as shown below. +As the new message activity arrives, the bot runs the `MainDialog`. The `MainDialog` prompts the user for what it can help with. And then it starts the `bookingDialog` in the `act_step` method, with a call to `begin_dialog` as shown below. -[!code-python[act step](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/main_dialog.py?range=63-100&highlight=4-5,20)] +[!code-python[act step](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/main_dialog.py?range=63-100&highlight=4-6,20)] -Next, in the `final_step` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or cancelled. +Next, in the `final_step` method of the `MainDialog` class, the booking dialog ended and the booking is considered to be complete or canceled. [!code-python[final step](~/../botbuilder-samples/samples/python/13.core-bot/dialogs/main_dialog.py?range=102-118)] @@ -147,29 +188,33 @@ Next, in the `final_step` method of the `MainDialog` class, the booking dialog e ## Handle unexpected errors -Next, we deal with any unhandled exceptions that might occur. +The adapter's error handler handles any exceptions that weren't caught in the bot. # [C#](#tab/csharp) **AdapterWithErrorHandler.cs** -In our sample, the adapter's `OnTurnError` handler receives any exceptions thrown by your bot's turn logic. If there is an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in a error-loop caused by being in a bad state. +In the sample, the adapter's `OnTurnError` handler receives any exceptions thrown by your bot's turn logic. If there's an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in an error loop caused by being in a bad state. -[!code-csharp[AdapterWithErrorHandler](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/AdapterWithErrorHandler.cs?range=19-50)] +[!code-csharp[AdapterWithErrorHandler](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/AdapterWithErrorHandler.cs?range=20-54)] # [JavaScript](#tab/javascript) **index.js** -In our sample, the adapter's `onTurnError` handler receives any exceptions thrown by your bot's turn logic. If there is an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in a error-loop caused by being in a bad state. +In the sample, the adapter's `onTurnError` handler receives any exceptions thrown by your bot's turn logic. If there's an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in an error loop caused by being in a bad state. + +[!code-javascript[AdapterWithErrorHandler](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/index.js?range=50-73)] + +# [Java](#tab/java) -[!code-javascript[AdapterWithErrorHandler](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/index.js?range=35-57)] +By registering an `AdapterWithErrorHandler` with the Spring framework in **Application.java** for the `BotFrameworkHttpAdapter` in this sample, the adapter's `onTurnError` handler receives any exceptions thrown by your bot's turn logic. If there's an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in an error loop caused by being in a bad state. In the Java SDK, the `AdapterWithErrorHandler` is implemented as part of the SDK and is included in the **com.microsoft.bot.integration** package. See the Java SDK source code for details on the implementation of this adapter. ## [Python](#tab/python) **adapter_with_error_handler.py** -In our sample, the adapter's `on_error` handler receives any exceptions thrown by your bot's turn logic. If there is an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in a error-loop caused by being in a bad state. +In the sample, the adapter's `on_error` handler receives any exceptions thrown by your bot's turn logic. If there's an exception thrown, the handler deletes the conversation state for the current conversation to prevent the bot from getting stuck in an error loop caused by being in a bad state. [!code-python[adapter_with_error_handler](~/../botbuilder-samples/samples/python/13.core-bot/adapter_with_error_handler.py?range=16-56)] @@ -187,9 +232,9 @@ Finally, in `Startup.cs`, the bot is created as a transient, and on every turn, For reference, here are the class definitions that are used in the call to create the bot above. -[!code-csharp[MainDialog signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=17)] [!code-csharp[DialogAndWelcomeBot signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Bots/DialogAndWelcomeBot.cs?range=16)] -[!code-csharp[DialogBot signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Bots/DialogBot.cs?range=18)] +[!code-csharp[DialogBot signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Bots/DialogBot.cs?range=18-19)] +[!code-csharp[MainDialog signature](~/../botbuilder-samples/samples/csharp_dotnetcore/13.core-bot/Dialogs/MainDialog.cs?range=17)] # [JavaScript](#tab/javascript) @@ -197,20 +242,36 @@ For reference, here are the class definitions that are used in the call to creat Finally, in `index.js`, the bot is created. -[!code-javascript[Create bot](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/index.js?range=78-81)] +[!code-javascript[Create recognizer, dialogs, and bot](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/index.js?range=88-97)] For reference, here are the class definitions that are used in the call to create the bot above. -[!code-javascript[MainDialog signature](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=11)] +[!code-javascript[MainDialog signature](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/dialogs/mainDialog.js?range=12)] + [!code-javascript[DialogAndWelcomeBot signature](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/bots/dialogAndWelcomeBot.js?range=8)] + [!code-javascript[DialogBot signature](~/../botbuilder-samples/samples/javascript_nodejs/13.core-bot/bots/dialogBot.js?range=6)] +# [Java](#tab/java) + +**Application.java** + +Finally, in `Application.java`, the bot is created. + +[!code-java[Add transient bot](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/Application.java?range=57-66)] + +For reference, here are the class definitions that are used in the call to create the bot above. + +[!code-java[DialogAndWelcomeBot signature](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/DialogAndWelcomeBot.java?range=31)] +[!code-java[DialogBot signature](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/DialogBot.java?range=26)] +[!code-java[MainDialog signature](~/../botbuilder-samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/MainDialog.java?range=32)] + ## [Python](#tab/python) **app.py** Finally, in `app.py`, the bot is created. -[!code-python[create bot](~/../botbuilder-samples/samples/python/13.core-bot/app.py?range=45-49)] +[!code-python[create bot](~/../botbuilder-samples/samples/python/13.core-bot/app.py?range=46-50)] For reference, here are the class definitions that are used in the call to create the bot. @@ -222,35 +283,26 @@ For reference, here are the class definitions that are used in the call to creat --- -## To test the bot +## Test the bot -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). 1. Run the sample locally on your machine. -1. Start the emulator, connect to your bot, and send messages as shown below. - - +1. Start the Emulator, connect to your bot, and send messages as shown below. ## Additional information -- The [authentication sample](https://aka.ms/logout) shows how to handle logout which uses similar pattern shown here for handling interruptions. +- The **24.bot-authentication-msgraph** sample in [C#](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/24.bot-authentication-msgraph#readme), [JavaScript](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/24.bot-authentication-msgraph#readme), [Python](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/24.bot-authentication-msgraph#readme), or [Java](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/24.bot-authentication-msgraph#readme) shows how to handle a logout request. It uses a pattern similar to the one shown here for handling interruptions. - You should send a default response instead of doing nothing and leaving the user wondering what is going on. The default response should tell the user what commands the bot understands so the user can get back on track. -- At any point in the turn, the turn context's _responded_ property indicates whether the bot has sent a message to the user this turn. Before the turn ends, your bot should send some message to the user, even if it is a simple acknowledgement of their input. - - +- At any point in the turn, the turn context's _responded_ property indicates whether the bot has sent a message to the user this turn. Before the turn ends, your bot should send some message to the user, even if it's a simple acknowledgment of their input. [concept-basics]: bot-builder-basics.md [concept-state]: bot-builder-concept-state.md [concept-dialogs]: bot-builder-concept-dialog.md - -[using-luis]: bot-builder-howto-v4-luis.md -[using-qna]: bot-builder-howto-qna.md - -[simple-flow]: bot-builder-dialog-manage-conversation-flow.md -[prompting]: bot-builder-prompts.md [component-dialogs]: bot-builder-compositcontrol.md -[cs-sample]: https://aka.ms/cs-core-sample -[js-sample]: https://aka.ms/js-core-sample -[python-sample]: https://aka.ms/bot-core-python-sample-code +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot#readme +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot#readme +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/13.core-bot#readme +[python-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/13.core-bot#readme diff --git a/articles/v4sdk/bot-builder-howto-long-operations-guidance.md b/articles/v4sdk/bot-builder-howto-long-operations-guidance.md new file mode 100644 index 000000000..c32d2a267 --- /dev/null +++ b/articles/v4sdk/bot-builder-howto-long-operations-guidance.md @@ -0,0 +1,506 @@ +--- +title: Manage a long-running operation +description: Learn how to avoid time-out issues and use proactive messages to handle long-running operations within a bot. +keywords: long operations, time out, 15 seconds +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to +monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen +--- + +# Manage a long-running operation + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Proper handling of long-running operations is an important aspect of a robust bot. When the Azure AI Bot Service sends an activity to your bot from a channel, the bot is expected to process the activity quickly. If the bot doesn't complete the operation within 10 to 15 seconds, depending on the channel, the Azure AI Bot Service will time out and report back to the client a `504:GatewayTimeout`, as described in [How bots work][concept-basics]. + +This article describes how to use an external service to execute the operation and to notify the bot when it has completed. + +## Prerequisites + +- If you don't have an Azure subscription, create a [free](https://azure.microsoft.com/free/) account before you begin. +- Familiarity with + [prompts in waterfall dialogs](bot-builder-concept-waterfall-dialogs.md#prompts) and + [proactive messaging](bot-builder-howto-proactive-message.md). +- Familiarity with + [Azure Queue Storage](/azure/storage/queues/storage-queues-introduction) and + [Azure Functions C# script](/azure/azure-functions/functions-reference-csharp). +- A copy of the **multi-turn prompt** sample in [C#](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt). + +## About this sample + +This article begins with the multi-turn prompt sample bot and adds code for performing long-running operations. It also demonstrates how to respond to a user after the operation has completed. In the updated sample: + +- The bot asks the user which long-running operation to perform. +- The bot receives an activity from the user, and determines which operation to perform. +- The bot notifies the user the operation will take some time and sends the operation to a C# function. + - The bot saves state, indicating there's an operation in progress. + - While the operation is running, the bot responds to messages from the user, notifying them the operation is still in progress. + - Azure Functions manages the long-running operation and sends an `event` activity to the bot, notifying it that the operation completed. +- The bot resumes the conversation and sends a proactive message to notify the user that the operation completed. The bot then clears the operation state mentioned earlier. + +This example defines a `LongOperationPrompt` class, derived from the abstract `ActivityPrompt` class. When the `LongOperationPrompt` queues the activity to be processed, it includes a choice from the user within the activity's _value_ property. This activity is then consumed by Azure Functions, modified, and wrapped in a different `event` activity before it's sent back to the bot using a Direct Line client. Within the bot, the event activity is used to resume the conversation by calling the adapter's _continue conversation_ method. The dialog stack is then loaded, and the `LongOperationPrompt` completes. + +This article touches on many different technologies. See the [additional information](#additional-information) section for links to associated articles. + +[!INCLUDE [ropc-connection-strings](../includes/ropc-connection-strings-important.md)] + +## Create an Azure Storage account + +Create an Azure Storage account, and retrieve the connection string. You'll need to add the connection string to your bot's configuration file. + +For more information, see [create a storage account](/azure/storage/common/storage-account-create) and [copy your credentials from the Azure portal](/azure/storage/queues/storage-dotnet-how-to-use-queues?tabs=dotnet#copy-your-credentials-from-the-azure-portal). + +## Create a bot resource + +1. Setup Dev Tunnels and retrieve a URL to be used as the bot's _messaging endpoint_ during local debugging. The messaging endpoint will be the HTTPS forwarding URL with `/api/messages/` appended—the default port for new bots is 3978. + + For more information, see how to [debug a bot using devtunnel](../bot-service-debug-channel-devtunnel.md). + +1. Create an Azure Bot resource in the Azure portal or with the Azure CLI. Set the bot's messaging endpoint to the one you created with Dev Tunnels. After the bot resource is created, obtain the bot's Microsoft app ID and password. Enable the Direct Line channel, and retrieve a Direct Line secret. You'll add these to your bot code and C# function. + + For more information, see how to [manage a bot](../bot-service-manage-overview.md) and how to [connect a bot to Direct Line](../bot-service-channel-connect-directline.md). + +## Create the C# function + +1. Create an Azure Functions app based on the .NET Core runtime stack. + + For more information, see how to [create a function app](/azure/azure-functions/functions-create-function-app-portal) and the [Azure Functions C# script reference](/azure/azure-functions/functions-reference-csharp). + +1. Add a `DirectLineSecret` application setting to the Function App. + + For more information, see how to [manage your function app](/azure/azure-functions/functions-how-to-use-azure-function-app-settings). + +1. Within the Function App, add a function based on the [Azure Queue Storage template](/azure/azure-functions/functions-bindings-storage-queue-trigger). + + Set the desired queue name, and choose the `Azure Storage Account` created in an earlier step. This queue name will also be placed in the bot's **appsettings.json** file. + +1. Add a **function.proj** file to the function. + + ```xml + + + netstandard2.0 + + + + + + + + ``` + +1. Update **run.csx** with the following code: + + ```csharp + #r "Newtonsoft.Json" + + using System; + using System.Net.Http; + using System.Text; + using Newtonsoft.Json; + using Microsoft.Bot.Connector.DirectLine; + using System.Threading; + + public static async Task Run(string queueItem, ILogger log) + { + log.LogInformation($"C# Queue trigger function processing"); + + JsonSerializerSettings jsonSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }; + var originalActivity = JsonConvert.DeserializeObject(queueItem, jsonSettings); + // Perform long operation here.... + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(15)); + + if(originalActivity.Value.ToString().Equals("option 1", StringComparison.OrdinalIgnoreCase)) + { + originalActivity.Value = " (Result for long operation one!)"; + } + else if(originalActivity.Value.ToString().Equals("option 2", StringComparison.OrdinalIgnoreCase)) + { + originalActivity.Value = " (A different result for operation two!)"; + } + + originalActivity.Value = "LongOperationComplete:" + originalActivity.Value; + var responseActivity = new Activity("event"); + responseActivity.Value = originalActivity; + responseActivity.Name = "LongOperationResponse"; + responseActivity.From = new ChannelAccount("GenerateReport", "AzureFunction"); + + var directLineSecret = Environment.GetEnvironmentVariable("DirectLineSecret"); + using(DirectLineClient client = new DirectLineClient(directLineSecret)) + { + var conversation = await client.Conversations.StartConversationAsync(); + await client.Conversations.PostActivityAsync(conversation.ConversationId, responseActivity); + } + + log.LogInformation($"Done..."); + } + ``` + +## Create the bot + +1. Start with a copy of the C# [Multi-Turn-Prompt](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/05.multi-turn-prompt) sample. +1. Add the **Azure.Storage.Queues** NuGet package to your project. +1. Add the connection string for the Azure Storage account you created earlier, and the Storage Queue Name, to your bot's configuration file. + + Ensure the queue name is the same as the one you used to create the Queue Trigger Function earlier. Also add the values for the `MicrosoftAppId` and `MicrosoftAppPassword` properties that you generated earlier when you created the Azure Bot resource. + + **appsettings.json** + + ```json + { + "MicrosoftAppId": "", + "MicrosoftAppPassword": "", + "StorageQueueName": "", + "QueueStorageConnection": "" + } + ``` + +1. Add an `IConfiguration` parameter to **DialogBot.cs** in order to retrieve the `MicrsofotAppId`. Also add an `OnEventActivityAsync` handler for the `LongOperationResponse` from the Azure Function. + + **Bots\DialogBot.cs** + + ```csharp + protected readonly IStatePropertyAccessor DialogState; + protected readonly Dialog Dialog; + protected readonly BotState ConversationState; + protected readonly ILogger Logger; + private readonly string _botId; + + /// + /// Create an instance of . + /// + /// used to retrieve MicrosoftAppId + /// which is used in ContinueConversationAsync. + /// used to store the DialogStack. + /// The RootDialog for this bot. + /// to use. + public DialogBot(IConfiguration configuration, ConversationState conversationState, T dialog, ILogger> logger) + { + _botId = configuration["MicrosoftAppId"] ?? Guid.NewGuid().ToString(); + ConversationState = conversationState; + Dialog = dialog; + Logger = logger; + DialogState = ConversationState.CreateProperty(nameof(DialogState)); + } + + public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) + { + await base.OnTurnAsync(turnContext, cancellationToken); + + // Save any state changes that might have occurred during the turn. + await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); + } + + protected override async Task OnEventActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) + { + // The event from the Azure Function will have a name of 'LongOperationResponse' + if (turnContext.Activity.ChannelId == Channels.Directline && turnContext.Activity.Name == "LongOperationResponse") + { + // The response will have the original conversation reference activity in the .Value + // This original activity was sent to the Azure Function via Azure.Storage.Queues in AzureQueuesService.cs. + var continueConversationActivity = (turnContext.Activity.Value as JObject)?.ToObject(); + await turnContext.Adapter.ContinueConversationAsync(_botId, continueConversationActivity.GetConversationReference(), async (context, cancellation) => + { + Logger.LogInformation("Running dialog with Activity from LongOperationResponse."); + + // ContinueConversationAsync resets the .Value of the event being continued to Null, + //so change it back before running the dialog stack. (The .Value contains the response + //from the Azure Function) + context.Activity.Value = continueConversationActivity.Value; + await Dialog.RunAsync(context, DialogState, cancellationToken); + + // Save any state changes that might have occurred during the inner turn. + await ConversationState.SaveChangesAsync(context, false, cancellationToken); + }, cancellationToken); + } + else + { + await base.OnEventActivityAsync(turnContext, cancellationToken); + } + } + ``` + +1. Create an Azure Queues service to queue activities to be processed. + + **AzureQueuesService.cs** + + ```csharp + /// + /// Service used to queue messages to an Azure.Storage.Queues. + /// + public class AzureQueuesService + { + private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings() + { + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore + }; + + private bool _createQueuIfNotExists = true; + private readonly QueueClient _queueClient; + + /// + /// Creates a new instance of . + /// + /// used to retrieve + /// StorageQueueName and QueueStorageConnection from appsettings.json. + public AzureQueuesService(IConfiguration config) + { + var queueName = config["StorageQueueName"]; + var connectionString = config["QueueStorageConnection"]; + + _queueClient = new QueueClient(connectionString, queueName); + } + + /// + /// Queue and Activity, with option in the Activity.Value to Azure.Storage.Queues + /// + /// + /// + /// Activity to queue after a call to GetContinuationActivity. + /// The option the user chose, which will be passed within the .Value of the activity queued. + /// Cancellation token for the async operation. + /// Queued . + public async Task QueueActivityToProcess(Activity referenceActivity, string option, CancellationToken cancellationToken) + { + if (_createQueuIfNotExists) + { + _createQueuIfNotExists = false; + await _queueClient.CreateIfNotExistsAsync().ConfigureAwait(false); + } + + // create ContinuationActivity from the conversation reference. + var activity = referenceActivity.GetConversationReference().GetContinuationActivity(); + // Pass the user's choice in the .Value + activity.Value = option; + + var message = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(activity, jsonSettings))); + + // Aend ResumeConversation event, it will get posted back to us with a specific value, giving us + // the ability to process it and do the right thing. + var reciept = await _queueClient.SendMessageAsync(message, cancellationToken).ConfigureAwait(false); + return reciept.Value.MessageId; + } + } + ``` + +## Dialogs + +Remove the old dialog and replace it with new dialogs to support the operations. + +1. Remove the **UserProfileDialog.cs** file. +1. Add a custom prompt dialog that asks the user which operation to perform. + + **Dialogs\LongOperationPrompt.cs** + + ```csharp + /// + /// implementation which will queue an activity, + /// along with the , + /// and wait for an with name of "ContinueConversation" + /// and Value containing the text: "LongOperationComplete". + /// + /// The result of this prompt will be the received Event Activity, which is sent by + /// the Azure Function after it finishes the long operation. + /// + public class LongOperationPrompt : ActivityPrompt + { + private readonly AzureQueuesService _queueService; + + /// + /// Create a new instance of . + /// + /// Id of this . + /// Validator to use for this prompt. + /// to use for Enqueuing the activity to process. + public LongOperationPrompt(string dialogId, PromptValidator validator, AzureQueuesService queueService) + : base(dialogId, validator) + { + _queueService = queueService; + } + + public async override Task BeginDialogAsync(DialogContext dc, object options, CancellationToken cancellationToken = default) + { + // When the dialog begins, queue the option chosen within the Activity queued. + await _queueService.QueueActivityToProcess(dc.Context.Activity, (options as LongOperationPromptOptions).LongOperationOption, cancellationToken); + + return await base.BeginDialogAsync(dc, options, cancellationToken); + } + + protected override Task> OnRecognizeAsync(ITurnContext turnContext, IDictionary state, PromptOptions options, CancellationToken cancellationToken = default) + { + var result = new PromptRecognizerResult() { Succeeded = false }; + + if(turnContext.Activity.Type == ActivityTypes.Event + && turnContext.Activity.Name == "ContinueConversation" + && turnContext.Activity.Value != null + // Custom validation within LongOperationPrompt. + // 'LongOperationComplete' is added to the Activity.Value in the Queue consumer (See: Azure Function) + && turnContext.Activity.Value.ToString().Contains("LongOperationComplete", System.StringComparison.InvariantCultureIgnoreCase)) + { + result.Succeeded = true; + result.Value = turnContext.Activity; + } + + return Task.FromResult(result); + } + } + ``` + +1. Add a prompt options class for the custom prompt. + + **Dialogs\LongOperationPromptOptions.cs** + + ```csharp + /// + /// Options sent to demonstrating how a value + /// can be passed along with the queued activity. + /// + public class LongOperationPromptOptions : PromptOptions + { + /// + /// This is a property sent through the Queue, and is used + /// in the queue consumer (the Azure Function) to differentiate + /// between long operations chosen by the user. + /// + public string LongOperationOption { get; set; } + } + ``` + +1. Add the dialog that uses the custom prompt to get the user's choice and initiates the long-running operation. + + **Dialogs\LongOperationDialog.cs** + + ```csharp + /// + /// This dialog demonstrates how to use the . + /// + /// The user is provided an option to perform any of three long operations. + /// Their choice is then sent to the . + /// When the prompt completes, the result is received as an Activity in the + /// final Waterfall step. + /// + public class LongOperationDialog : ComponentDialog + { + public LongOperationDialog(AzureQueuesService queueService) + : base(nameof(LongOperationDialog)) + { + // This array defines how the Waterfall will execute. + var waterfallSteps = new WaterfallStep[] + { + OperationTimeStepAsync, + LongOperationStepAsync, + OperationCompleteStepAsync, + }; + + // Add named dialogs to the DialogSet. These names are saved in the dialog state. + AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps)); + AddDialog(new LongOperationPrompt(nameof(LongOperationPrompt), (vContext, token) => + { + return Task.FromResult(vContext.Recognized.Succeeded); + }, queueService)); + AddDialog(new ChoicePrompt(nameof(ChoicePrompt))); + + // The initial child Dialog to run. + InitialDialogId = nameof(WaterfallDialog); + } + + private static async Task OperationTimeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + { + // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it's a Prompt Dialog. + // Running a prompt here means the next WaterfallStep will be run when the user's response is received. + return await stepContext.PromptAsync(nameof(ChoicePrompt), + new PromptOptions + { + Prompt = MessageFactory.Text("Please select a long operation test option."), + Choices = ChoiceFactory.ToChoices(new List { "option 1", "option 2", "option 3" }), + }, cancellationToken); + } + + private static async Task LongOperationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + { + var value = ((FoundChoice)stepContext.Result).Value; + stepContext.Values["longOperationOption"] = value; + + var prompt = MessageFactory.Text("...one moment please...."); + // The reprompt will be shown if the user messages the bot while the long operation is being performed. + var retryPrompt = MessageFactory.Text($"Still performing the long operation: {value} ... (is the Azure Function executing from the queue?)"); + return await stepContext.PromptAsync(nameof(LongOperationPrompt), + new LongOperationPromptOptions + { + Prompt = prompt, + RetryPrompt = retryPrompt, + LongOperationOption = value, + }, cancellationToken); + } + + private static async Task OperationCompleteStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) + { + stepContext.Values["longOperationResult"] = stepContext.Result; + await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Thanks for waiting. { (stepContext.Result as Activity).Value}"), cancellationToken); + + // Start over by replacing the dialog with itself. + return await stepContext.ReplaceDialogAsync(nameof(WaterfallDialog), null, cancellationToken); + } + } + ``` + +## Register services and Dialog + +In **Startup.cs**, update the `ConfigureServices` method to register the `LongOperationDialog` and add the `AzureQueuesService`. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddControllers().AddNewtonsoftJson(); + + // Create the Bot Framework Adapter with error handling enabled. + services.AddSingleton(); + + // In production, this should be a persistent storage provider.bot + services.AddSingleton(new MemoryStorage()); + + // Create the Conversation state. (Used by the Dialog system itself.) + services.AddSingleton(); + + // The Dialog that will be run by the bot. + services.AddSingleton(); + + // Service used to queue into Azure.Storage.Queues + services.AddSingleton(); + + // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. + services.AddTransient>(); +} +``` + +## To test the bot + +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). +1. Run the sample locally on your machine. +1. Start the Emulator and connect to your bot. +1. Choose a long operation to start. + - The bot sends a _one moment, please_ message and queues the Azure function. + - If the user tries to interact with the bot before the operation completes, the bot replies with a _still working_ message. + - Once the operation completes, the bot sends a proactive message to the user to let them know it finished. + +:::image type="content" source="./media/how-to-long-operations/long-operations-bot-example.png" alt-text="Sample transcript with the user initiating a long operation and eventually receiving a proactive message that the operation completed."::: + +## Additional information + +| Tool or feature | Resources | +|:-|:-| +| Azure Functions | [Create a function app](/azure/azure-functions/functions-create-function-app-portal)
[Azure Functions C# script](/azure/azure-functions/functions-reference-csharp)
[Manage your function app](/azure/azure-functions/functions-how-to-use-azure-function-app-settings) | +| Azure portal | [Manage a bot](../bot-service-manage-overview.md)
[Connect a bot to Direct Line](../bot-service-channel-connect-directline.md) | +| Azure Storage | [Azure Queue Storage](/azure/storage/queues/storage-queues-introduction)
[Create a storage account](/azure/storage/common/storage-account-create)
[Copy your credentials from the Azure portal](/azure/storage/queues/storage-dotnet-how-to-use-queues?tabs=dotnet#copy-your-credentials-from-the-azure-portal)
[How to Use Queues](/azure/storage/queues/storage-dotnet-how-to-use-queues) | +| Bot basics | [How bots work][concept-basics]
[Prompts in waterfall dialogs](bot-builder-concept-waterfall-dialogs.md#prompts)
[Proactive messaging](bot-builder-howto-proactive-message.md) | +| Dev Tunnels | [Debug a bot using devtunnel](../bot-service-debug-channel-devtunnel.md) | + +[concept-basics]: bot-builder-basics.md +[concept-dialogs]: bot-builder-concept-dialog.md diff --git a/articles/v4sdk/bot-builder-howto-proactive-message.md b/articles/v4sdk/bot-builder-howto-proactive-message.md index b6269e3ac..e7ee47aa8 100644 --- a/articles/v4sdk/bot-builder-howto-proactive-message.md +++ b/articles/v4sdk/bot-builder-howto-proactive-message.md @@ -1,45 +1,68 @@ --- -title: Send proactive notifications to users - Bot Service -description: Understand how to send notification messages -keywords: proactive message, notification message, bot notification, -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/24/2020 +title: Send proactive notifications to users +description: Learn how bots send notification messages. See how to retrieve conversation references and test proactive messages. View code samples and design considerations. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - abs-meta-21q1 + - evergreen monikerRange: 'azure-bot-service-4.0' --- # Send proactive notifications to users -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Typically, each message that a bot sends to the user directly relates to the user's prior input. -In some cases, a bot may need to send the user a message that is not directly related to the current topic of conversation or to the last message the user sent. These types of messages are called _proactive messages_. +Typically, a bot sends a message to a user directly in response to receiving a message from the user. +Occasionally, a bot might need to send a _proactive message_, a message in response to stimulus not originating from the user. -Proactive messages can be useful in a variety of scenarios. For example, if the user has previously asked the bot to monitor the price of a product, the bot can alert the user if the price of the product has dropped by 20%. Or, if a bot requires some time to compile a response to the user's question, it may inform the user of the delay and allow the conversation to continue in the meantime. When the bot finishes compiling the response to the question, it will share that information with the user. +Proactive messages can be useful in various scenarios. For example, if the user has previously asked the bot to monitor the price of a product, the bot can alert the user if the price of the product has dropped by 20%. Or, if a bot requires some time to compile a response to the user's question, it may inform the user of the delay and allow the conversation to continue in the meantime. When the bot finishes compiling the response to the question, it will share that information with the user. -When implementing proactive messages in your bot, don't send several proactive messages within a short amount of time. Some channels enforce restrictions on how frequently a bot can send messages to the user, and will disable the bot if it violates those restrictions. +This article covers information about proactive messages for bots in general. For information about proactive messages in Microsoft Teams, see -An ad hoc proactive message is the simplest type of proactive message. The bot simply interjects the message into the conversation whenever it is triggered, without any regard for whether the user is currently engaged in a separate topic of conversation with the bot and will not attempt to change the conversation in any way. +- The **Teams conversation bot** sample in [**C#**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/57.teams-conversation-bot#readme), [**JavaScript**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/57.teams-conversation-bot#readme), [**Java**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/57.teams-conversation-bot#readme), or [**Python**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/57.teams-conversation-bot#readme). +- The Microsoft Teams documentation on how to [send proactive messages](/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages). -To handle notifications more smoothly, consider other ways to integrate the notification into the conversation flow, such as setting a flag in the conversation state or adding the notification to a queue. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Understand [bot basics](bot-builder-basics.md). -- A copy of the proactive messages sample in [**C#**](https://aka.ms/proactive-sample-cs) or [**JavaScript**](https://aka.ms/proactive-sample-js) or [**Python**](https://aka.ms/bot-proactive-python-sample-code). The sample is used to explain proactive messaging in this article. +- A copy of the **proactive messages** sample in [**C#**](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages#readme), [**JavaScript**](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/16.proactive-messages#readme), [**Java**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/16.proactive-messages#readme), or [**Python**](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/16.proactive-messages#readme). The sample is used to explain proactive messaging in this article. ## About the proactive sample -The sample has a bot and an additional controller that is used to send proactive messages to the bot, as shown in the following illustration. +In general, a bot as an application has a few layers: + +- The web application that can accept HTTP requests and specifically supports a messaging endpoint. +- An adapter that handles connectivity with the channels. +- A handler for the turn, typically encapsulated in a _bot_ class that handles the conversational reasoning for the bot app. + +In response to an incoming message from the user, the app calls the adapter's _process activity_ method, which creates a turn and turn context, calls its middleware pipeline, and then calls the bot's turn handler. + +To initiate a proactive message, the bot application needs to be able to receive other input. +The application logic for initiating a proactive message is outside the scope of the SDK. +For this sample, a _notify_ endpoint, in addition to a standard _messages_ endpoint, is used to trigger the proactive turn. + +In response to a GET request on this notify endpoint, the app calls the adapter's _continue conversation_ method, which behaves similarly to the _process activity_ method. The _continue conversation_ method: + +- Takes an appropriate conversation reference for the user and the callback method to use for the proactive turn. +- Creates an event activity and turn context for the proactive turn. +- Calls the adapter's middleware pipeline. +- Calls the provided callback method. +- The turn context uses the conversation reference to send any messages to the user. -![proactive bot](media/proactive-sample-bot.png) +The sample has a bot, a messages endpoint, and an extra endpoint that's used to send proactive messages to the user, as shown in the following illustration. -## Retrieve and store conversation reference +:::image type="content" source="media/proactive-sample-bot.png" alt-text="Interaction diagram showing how the bot gets a conversation reference and uses it to send a proactive message."::: -When the emulator connects to the bot, the bot receives two conversation update activities. In the bot's conversation update activity handler, the conversation reference is retrieved and stored in a dictionary as shown below. +## Retrieve and store the conversation reference + +When the Bot Framework Emulator connects to the bot, the bot receives two conversation update activities. In the bot's conversation update activity handler, the conversation reference is retrieved and stored in a dictionary as shown below. # [C#](#tab/csharp) @@ -55,6 +78,12 @@ When the emulator connects to the bot, the bot receives two conversation update [!code-javascript[onConversationUpdateActivity](~/../botbuilder-samples/samples/javascript_nodejs/16.proactive-messages/bots/proactiveBot.js?range=41-44&highlight=2-3)] +# [Java](#tab/java) + +**ProactiveBot.java** + +[!code-java[OnConversationUpdateActivityAsync](~/../botbuilder-samples/samples/java_springboot/16.proactive-messages/src/main/java/com/microsoft/bot/sample/proactive/ProactiveBot.java?range=74-84&highlight=3,9-10)] + # [Python](#tab/python) **bots/proactive_bot.py** @@ -64,17 +93,23 @@ When the emulator connects to the bot, the bot receives two conversation update --- -Note: In a real-world scenario you would persist conversation references in a database instead of using an object in memory. +The conversation reference includes a _conversation_ property that describes the conversation in which the activity exists. The conversation includes a _user_ property that lists the users participating in the conversation, and a _service URL_ property that indicates where replies to the current activity may be sent. A valid conversation reference is needed to send proactive messages to users. (For the Teams channel, the service URL maps to a regionalized server.) -The conversation reference has a _conversation_ property that describes the conversation in which the activity exists. The conversation has a _user_ property that lists the users participating in the conversation, and a _service URL_ property that channels use to denote the URL where replies to the current activity may be sent. A valid conversation reference is needed to send proactive messages to users. +> [!NOTE] +> In a real-world scenario you would persist conversation references in a database instead of using an object in memory. -## Send proactive message +## Send a proactive message -The second controller, the _notify_ controller, is responsible for sending the proactive message to the bot. Use the following steps to generate a proactive message. +The second controller, the _notify_ controller, is responsible for sending the proactive message to the user. It uses the following steps to generate a proactive message. -1. Retrieve the reference for the conversation to which to send the proactive message. -1. Call the adapter's _continue conversation_ method, providing the conversation reference and the turn handler delegate to use. The continue conversation method generates a turn context for the referenced conversation and then calls the specified turn handler delegate. -1. In the delegate, use the turn context to send the proactive message. +1. Retrieves the reference for the conversation to which to send the proactive message. +1. Calls the adapter's _continue conversation_ method, providing the conversation reference and the turn handler delegate to use. (The continue conversation method generates a turn context for the referenced conversation and then calls the specified turn handler delegate.) +1. In the delegate, uses the turn context to send the proactive message. Here, the delegate is defined on the notify controller, and it sends the proactive message to the user. + +> [!NOTE] +> While each channel should use a stable service URL, the URL can change over time. For more information about the service URL, see the [Basic activity structure](https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md#basic-activity-structure) and [Service URL](https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md#service-url) sections of the Bot Framework Activity Schema. +> +> If the service URL changes, previous conversation references will no longer be valid and calls to _continue conversation_ will generate an error or exception. In this case, your bot will need to acquire a new conversation reference for the user before it can send proactive messages again. # [C#](#tab/csharp) @@ -83,9 +118,7 @@ The second controller, the _notify_ controller, is responsible for sending the p Each time the bot's notify page is requested, the notify controller retrieves the conversation references from the dictionary. The controller then uses the `ContinueConversationAsync` and `BotCallback` methods to send the proactive message. -[!code-csharp[Notify logic](~/../botbuilder-samples/samples/csharp_dotnetcore/16.proactive-messages/Controllers/NotifyController.cs?range=17-62&highlight=28,40-44)] - -To send a proactive message, the adapter requires an app ID for the bot. In a production environment, you can use the bot's app ID. In a local test environment, you can use any GUID. If the bot is not currently assigned an app ID, the notify controller self-generates a placeholder ID to use for the call. +[!code-csharp[Notify logic](~/../botbuilder-samples/samples/csharp_dotnetcore/16.proactive-messages/Controllers/NotifyController.cs?range=17-52&highlight=20,32-35)] # [JavaScript](#tab/javascript) @@ -95,65 +128,70 @@ Each time the server's `/api/notify` page is requested, the server retrieves the The server then uses the `continueConversation` method to send the proactive message. The parameter to `continueConversation` is a function that serves as the bot's turn handler for this turn. -[!code-javascript[Notify logic](~/../botbuilder-samples/samples/javascript_nodejs/16.proactive-messages/index.js?range=68-82&highlight=4-8)] +[!code-javascript[Notify logic](~/../botbuilder-samples/samples/javascript_nodejs/16.proactive-messages/index.js?range=80-92&highlight=4-6)] + +# [Java](#tab/java) + +**NotifyController.java** + +Each time the bot's notify page is requested, the notify controller retrieves the conversation references from the dictionary. +The controller then uses the `continueConversation` method to send the proactive message. + +[!code-java[Notify logic](~/../botbuilder-samples/samples/java_springboot/16.proactive-messages/src/main/java/com/microsoft/bot/sample/proactive/NotifyController.java?range=26-64&highlight=28-30)] # [Python](#tab/python) Each time the bot's notify page is requested, the server retrieves the conversation references from the dictionary. The server then uses the `_send_proactive_message` to send the proactive message. -[!code-python[Notify logic](~/../botbuilder-samples/samples/python/16.proactive-messages/app.py?range=97-105&highlight=5-9)] +[!code-python[Notify logic](~/../botbuilder-samples/samples/python/16.proactive-messages/app.py?range=98-106&highlight=5-9)] --- +To send a proactive message, the adapter requires an app ID for the bot. In a production environment, you can use the bot's app ID. To test the bot locally with the Emulator, you can use the empty string (""). + ## Test your bot -1. If you have not done so already, install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme). +1. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md). 1. Run the sample locally on your machine. -1. Start the emulator and connect to your bot. -1. Load to your bot's api/notify page. This will generate a proactive message in the emulator. +1. Start the Emulator and connect to your bot. +1. Load to your bot's api/notify page. This will generate a proactive message in the Emulator. ## Additional information -Besides the sample used in this article, additional samples are available in C# and JS on [GitHub](https://github.com/Microsoft/BotBuilder-Samples/). - -### Avoiding 401 "Unauthorized" Errors - -By default, the BotBuilder SDK adds a `serviceUrl` to the list of trusted host names if the incoming request is authenticated by BotAuthentication. They are maintained in an in-memory cache. If your bot is restarted, a user awaiting a proactive message cannot receive it unless they have messaged the bot again after it restarted. +### Requirements -To avoid this, you must manually add the `serviceUrl` to the list of trusted host names by using: +Before you can send a proactive message, your bot needs a _conversation reference_. Your bot can retrieve the conversation reference from any activity it has received from the user, but this typically requires the user to interact with the bot at least once before the bot can send a proactive message. -# [C#](#tab/csharp) - -```csharp -MicrosoftAppCredentials.TrustServiceUrl(serviceUrl); -``` +Many channels prohibit a bot from messaging a user unless the user has messaged the bot at least once. Some channels allow exceptions. For instance, the Teams channel allows your bot to send a proactive (or 1-on-1) message to individuals in an already established group conversation that includes the bot. -For proactive messaging, `serviceUrl` is the URL of the channel that the recipient of the proactive message is using and can be found in `Activity.ServiceUrl`. +### Design considerations -You'll want to add the above code just prior to the the code that sends the proactive message. In the [Proactive Messages Sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages), you would put it in `NotifyController.cs` just before `await turnContext.SendActivityAsync("proactive hello");`. +When implementing proactive messages in your bot, don't send several proactive messages within a short amount of time. Some channels enforce restrictions on how frequently a bot can send messages to the user, and will disable the bot if it violates those restrictions. -# [JavaScript](#tab/javascript) +For the simplest type of proactive message, the bot interjects the message into the conversation when it's triggered, without regard for the current state or topic of conversation. In this scenario, the proactive message interrupts the normal flow of conversation. -```js -MicrosoftAppCredentials.trustServiceUrl(serviceUrl); -``` +To handle notifications more smoothly, consider other ways to integrate the notification into the conversation flow, such as setting a flag in the conversation state or adding the notification to a queue. -For proactive messaging, `serviceUrl` is the URL of the channel that the recipient of the proactive message is using and can be found in `activity.serviceUrl`. +### About the proactive turn -You'll want to add the above code just prior to the the code that sends the proactive message. In the [Proactive Messages Sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/16.proactive-messages), you would put it in `index.js` just before `await turnContext.sendActivity('proactive hello');`. +The _continue conversation_ method uses the conversation reference and a turn callback handler to: -# [Python](#tab/python) +1. Create a turn in which the bot application can send the proactive message. The adapter creates an `event` activity for this turn, with its name set to "ContinueConversation". +1. Send the turn through the adapter's middleware pipeline. +1. Call the turn callback handler to perform custom logic. -```python -MicrosoftAppCredentials.trustServiceUrl(serviceUrl) -``` +In the **proactive messages** sample, the turn callback handler is defined in the notify controller and sends the message directly to the conversation, without sending the proactive activity through the bot's normal turn handler. +The sample code also doesn't access or update the bot's state on the proactive turn. -For proactive messaging, `serviceUrl` is the URL of the channel that the recipient of the proactive message is using and can be found in `activity.serviceUrl`. +Many bots are stateful and use state to manage a conversation over multiple turns. +When the continue conversation method creates a turn context, the turn will have the correct user and conversation state associated with it, and you can integrate proactive turns into your bot's logic. +If you need the bot logic to be aware of the proactive message, you have a few options for doing so. You can: -You'll want to add the above code just prior to the the code that sends the proactive message. In the [Proactive Messages Sample](https://aka.ms/bot-proactive-python-sample-code), you add it in `app.py` prior sending the *proactive hello* message. +- Provide the bot's turn handler as the turn callback handler. The bot will then receive the "ContinueConversation" event activity. +- Use the turn callback handler to add information to the turn context first, and then call the bot's turn handler. ---- +In both of these cases, you'll need to design your bot logic to handle the proactive event. ## Next steps diff --git a/articles/v4sdk/bot-builder-howto-qna.md b/articles/v4sdk/bot-builder-howto-qna.md index 316fb7cf2..768652ab5 100644 --- a/articles/v4sdk/bot-builder-howto-qna.md +++ b/articles/v4sdk/bot-builder-howto-qna.md @@ -1,101 +1,126 @@ --- -title: Use QnA Maker to answer questions - Bot Service -description: Learn how to use QnA maker in your bot. -keywords: question and answer, QnA, FAQs, qna maker -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/06/2019 +title: Use QnA Maker to answer questions +description: Learn how bots can answer questions from users without parsing or interpreting the questions. See how to use QnA Maker for this task. +keywords: question and answer, QnA, FAQs, qna maker +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ROBOTS: NOINDEX +ms.custom: + - evergreen --- # Use QnA Maker to answer questions -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] QnA Maker provides a conversational question and answer layer over your data. This allows your bot to send a question to the QnA Maker and receive an answer without needing to parse and interpret the question intent. One of the basic requirements in creating your own QnA Maker service is to populate it with questions and answers. In many cases, the questions and answers already exist in content like FAQs or other documentation; other times, you may want to customize your answers to questions in a more natural, conversational way. +This article describes how to use an _existing_ QnA Maker knowledge base from your bot. + +For new bots, consider using the [question answering](bot-builder-concept-luis.md#question-answering) feature of Azure Cognitive Service for Language. For information, see [Use question answering to answer questions](../bot-builder-howto-answer-questions.md). + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + ## Prerequisites -- The code in this article is based on the QnA Maker sample. You'll need a copy of it either in **[C#](https://aka.ms/cs-qna)** or **[JavaScript](https://aka.ms/js-qna-sample)** or **[Python](https://aka.ms/bot-qna-python-sample-code)**. -- [QnA Maker](https://www.qnamaker.ai/) account -- Knowledge of [bot basics](bot-builder-basics.md), [QnA Maker](https://docs.microsoft.com/azure/cognitive-services/qnamaker/overview/overview), and [managing bot resources](bot-file-basics.md). +- A [QnA Maker](https://www.qnamaker.ai/) account and an existing QnA Maker knowledge base. +- Knowledge of [bot basics](bot-builder-basics.md) and [QnA Maker](/azure/ai-services/qnamaker/overview/overview). +- A copy of the **QnA Maker (simple)** sample in [**C#** (archived)][], [**JavaScript** (archived)][], [**Java** (archived)][], or [**Python** (archived)][]. ## About this sample -To use QnA Maker in your bot, you need to create a knowledge base in the [QnA Maker](https://www.qnamaker.ai/) portal, as shown in the next section. Your bot then can send the user's questions to the maker which provides the best answers. +To use QnA Maker in your bot, you need an existing knowledge base in the [QnA Maker](https://www.qnamaker.ai/) portal. Your bot then can use the knowledge base to answer the user's questions. + +For new bot development, consider using [Copilot Studio](/microsoft-copilot-studio/fundamentals-what-is-copilot-studio). +If you need to create a new knowledge base for a Bot Framework SDK bot, see the following Azure AI services articles: + +- [What is question answering?](/azure/ai-services/language-service/question-answering/overview) +- [Create an FAQ bot](/azure/ai-services/language-service/question-answering/tutorials/bot-service) +- [Azure Cognitive Language Services Question Answering client library for .NET](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/cognitivelanguage/Azure.AI.Language.QuestionAnswering#readme) ## [C#](#tab/cs) -![QnABot logic flow](./media/qnabot-logic-flow.png) +:::image type="content" source="./media/qnabot-logic-flow.png" alt-text="C# QnABot logic flow"::: -`OnMessageActivityAsync` is called for each user input received. When called, it accesses `_configuration` information stored within the sample code's `appsetting.json` file to find the value to connect to your pre-configured QnA Maker knowledge base. +`OnMessageActivityAsync` is called for each user input received. When called, it accesses configuration settings from the sample code's **appsetting.json** file to find the value to connect to your pre-configured QnA Maker knowledge base. ## [JavaScript](#tab/js) -![QnABot JS logic flow](./media/qnabot-js-logic-flow.png) - -`OnMessage` is called for each user input received. When called, it accesses your `qnamaker` connector that was pre-configured using values provided from your sample code's `.env` file. The qnamaker method `getAnswers` connects your bot to your external QnA Maker knowledge base. +:::image type="content" source="./media/qnabot-js-logic-flow.png" alt-text="JavaScript QnABot logic flow"::: -## [Python](#tab/python) +`OnMessage` is called for each user input received. When called, it accesses configuration settings from your sample code's **.env** file. The qnamaker method `getAnswers` connects your bot to your external QnA Maker knowledge base. -![QnABot JS logic flow](./media/qnabot-python-logic-flow.png) +## [Java](#tab/java) -`on_message_activity` is called for each user input received. When called, it accesses your `qna_maker` connector that was pre-configured using values provided from your sample code's `config.py` file. The method `qna_maker.getAnswers` connects your bot to your external QnA Maker knowledge base. +:::image type="content" source="./media/qnabot-logic-flow-java.png" alt-text="Java QnABot logic flow"::: ---- +`onMessageActivity` is called for each user input received. When called, it accesses configuration settings from the sample code's **application.properties** file to find the value to connect to your pre-configured QnA Maker knowledge base. -The user's input is sent to your knowledge base and the best returned answer is displayed back to your user. +## [Python](#tab/python) -## Create a QnA Maker service and publish a knowledge base +:::image type="content" source="./media/qnabot-python-logic-flow.png" alt-text="Python QnABot logic flow"::: -The first step is to create a QnA Maker service. Follow the steps listed in the QnA Maker [documentation](https://docs.microsoft.com/azure/cognitive-services/qnamaker/how-to/set-up-qnamaker-service-azure) to create the service in Azure. +`on_message_activity` is called for each user input received. When called, it accesses configuration settings from your sample code's **config.py** file. The method `qna_maker.getAnswers` connects your bot to your external QnA Maker knowledge base. -Next, you'll create a knowledge base using the `smartLightFAQ.tsv` file located in the CognitiveModels folder of the sample project. The steps to create, train, and publish your QnA Maker [knowledge base](https://docs.microsoft.com/azure/cognitive-services/qnamaker/quickstarts/create-publish-knowledge-base) are listed in the QnA Maker documentation. As you follow these steps, name your KB `qna`, and use the `smartLightFAQ.tsv` file to populate your KB. +--- -> Note. This article may also be used to access your own user developed QnA Maker knowledge base. +The user's input is sent to your knowledge base and the best returned answer is displayed back to your user. ## Obtain values to connect your bot to the knowledge base +> [!TIP] +> The QnA Maker documentation has instructions on how to [create, train, and publish your knowledge base](/azure/ai-services/qnamaker/quickstarts/create-publish-knowledge-base). + 1. In the [QnA Maker](https://www.qnamaker.ai/) site, select your knowledge base. -1. With your knowledge base open, select the **Settings**. Record the value shown for _service name_. This value is useful for finding your knowledge base of interest when using the QnA Maker portal interface. It is not used to connect your bot app to this knowledge base. -1. Scroll down to find **Deployment details** record the following values from the Postman sample HTTP request: +1. With your knowledge base open, select the **SETTINGS** tab. Record the value shown for _service name_. This value is useful for finding your knowledge base of interest when using the QnA Maker portal interface. It's not used to connect your bot app to this knowledge base. +1. Scroll down to find **Deployment details** and record the following values from the Postman sample HTTP request: - POST /knowledgebases/\/generateAnswer - - Host: \ // Full URL ending with /qnamaker + - Host: \ - Authorization: EndpointKey \ -The full URL string for your Hostname will look like "https://< >.azure.net/qnamaker". These three values will provide the information necessary for your app to connect to your QnA Maker knowledge base via your Azure QnA service. +Your host URL will start with `https://` and end with `/qnamaker`, such as `https://.azure.net/qnamaker`. Your bot needs the knowledge base ID, host URL, and endpoint key to connect to your QnA Maker knowledge base. ## Update the settings file -First, add the information required to access your knowledge base including hostname, endpoint key and knowledge base Id (kbId) into the settings file. These are the values you saved from the **Settings** tab of your knowledge base in QnA Maker. +First, add the information required to access your knowledge base—including host name, endpoint key and knowledge base ID (kbId)—to the settings file. These are the values you saved from the **SETTINGS** tab of your knowledge base in QnA Maker. -If you aren't deploying this for production, the app ID and password fields can be left blank. +If you aren't deploying this for production, you can leave your bot's app ID and password fields blank. > [!NOTE] -> If you are adding access to a QnA Maker knowledge base into an existing bot application, be sure to add informative titles for your QnA entries. The "name" value within this section provides the key required to access this information from within your app. +> To add a QnA Maker knowledge base into an existing bot application, be sure to add informative titles for your QnA entries. The "name" value within this section provides the key required to access this information from within your app. ## [C#](#tab/cs) -### Update your appsettings.json file +**appsettings.json** -[!code-csharp[appsettings](~/../botbuilder-samples/samples/csharp_dotnetcore/11.qnamaker/appsettings.json)] +[**C#** (archived)][] ## [JavaScript](#tab/js) -### Update your .env file +**.env** -[!code-javascript[.env file](~/../botbuilder-samples/samples/javascript_nodejs/11.qnamaker/.env)] +[**JavaScript** (archived)][] + +## [Java](#tab/java) + +**application.properties** + +[**Java** (archived)][] ## [Python](#tab/python) -### Update your config.py file +**config.py** -[!code-python[config.py](~/../botbuilder-samples/samples/python/11.qnamaker/config.py?range=10-18)] +[**Python** (archived)][] --- @@ -107,30 +132,39 @@ First, we create an object for accessing our QnA Maker knowledge base. Be sure that the **Microsoft.Bot.Builder.AI.QnA** NuGet package is installed for your project. -In **QnABot.cs**, in the `OnMessageActivityAsync` method, we create a QnAMaker instance. The `QnABot` class is also where the names of the connection information, saved in `appsettings.json` above, are pulled in. If you have chosen different names for your knowledge base connection information in your settings file, be sure to update the names here to reflect your chosen name. +In **QnABot.cs**, in the `OnMessageActivityAsync` method, create a QnAMaker instance. The `QnABot` class is also where the names of the connection information, saved in **appsettings.json** above, are pulled in. If you chose different names for your knowledge base connection information in your settings file, be sure to update the names here to reflect your chosen name. **Bots/QnABot.cs** -[!code-csharp[qna connection](~/../botbuilder-samples/samples/csharp_dotnetcore/11.qnamaker/Bots/QnABot.cs?range=32-39)] +[**C#** (archived)][] ## [JavaScript](#tab/js) Be sure that npm package **botbuilder-ai** is installed for your project. -In our sample the code for the bot logic is in the **QnABot.js** file. +In the sample, the code for the bot logic is in the **QnABot.js** file. In the **QnABot.js** file, we use the connection information provided by your .env file to establish a connection to the QnA Maker service: _this.qnaMaker_. **bots/QnABot.js** -[!code-javascript[QnAMaker](~/../botbuilder-samples/samples/javascript_nodejs/11.qnamaker/bots/QnABot.js?range=12-16)] +[**JavaScript** (archived)][] + +## [Java](#tab/java) + +In **QnABot.java**, in the `onMessageActivity` method, create a QnAMaker instance. The `QnABot` class is also where the names of the connection information, saved in **application.properties** above, are pulled in. If you chose different names for your knowledge base connection information in your settings file, be sure to update the names here to reflect your chosen name. + +**QnABot.java** + +[**Java** (archived)][] ## [Python](#tab/python) -In the **qna_bot.py** file, we use the connection information provided by the `config.py` file to establish a connection to the QnA Maker service: `self.qna_maker`. +In the **qna_bot.py** file, use the connection information provided by the **config.py** file to establish a connection to the QnA Maker service: `self.qna_maker`. **bots/qna_bot.py** -[!code-python[QnAMaker](~/../botbuilder-samples/samples/python/11.qnamaker/bots/qna_bot.py?range=13-19)] + +[**Python** (archived)][] --- @@ -138,11 +172,11 @@ In the **qna_bot.py** file, we use the connection information provided by the `c ## [C#](#tab/cs) -When your bot needs an answer from QnAMaker, call `GetAnswersAsync()` from your bot code to get the appropriate answer based on the current context. If you are accessing your own knowledge base, change the _no answers found_ message below to provide useful instructions for your users. +When your bot needs an answer from QnAMaker, call the `GetAnswersAsync` method from your bot code to get the appropriate answer based on the current context. If you're accessing your own knowledge base, change the _no answers found_ message below to provide useful instructions for your users. **Bots/QnABot.cs** -[!code-csharp[qna get answers](~/../botbuilder-samples/samples/csharp_dotnetcore/11.qnamaker/Bots/QnABot.cs?range=43-52)] +[**C#** (archived)][] ## [JavaScript](#tab/js) @@ -150,224 +184,62 @@ In the **QnABot.js** file, we pass the user's input to the QnA Maker service's ` **bots/QnABot.js** -[!code-javascript[OnMessage](~/../botbuilder-samples/samples/javascript_nodejs/11.qnamaker/bots/QnABot.js?range=46-55)] +[**JavaScript** (archived)][] + +## [Java](#tab/java) + +When your bot needs an answer from QnAMaker, call the `getAnswers` method from your bot code to get the appropriate answer based on the current context. If you're accessing your knowledge base, change the _no answers found_ message to provide helpful instructions for your users. + +**QnABot.java** + +[**Java** (archived)][] ## [Python](#tab/python) -In the **qna_bot.py** file, we pass the user's input to the QnA Maker service's `get_answers` method to get answers from the knowledge base. If QnA Maker returns a response, this is shown to the user. Otherwise, the user receives the message *No QnA Maker answers were found.* +In the **qna_bot.py** file, we pass the user's input to the QnA Maker service's `get_answers` method to get answers from the knowledge base. If QnA Maker returns a response, this is shown to the user. Otherwise, the user receives the message _No QnA Maker answers were found._ **bots/qna_bot.py** -[!code-python[get_answers](~/../botbuilder-samples/samples/python/11.qnamaker/bots/qna_bot.py?range=33-37)] + +[**Python** (archived)][] --- ## Test the bot -Run the sample locally on your machine. If you have not done so already, install the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md#download). For further instructions, refer to the readme file for [C# sample](https://aka.ms/cs-qna) or [Javascript sample](https://aka.ms/js-qna-sample). -or [Python sample](https://aka.ms/bot-qna-python-sample-code). +Run the sample locally on your machine. If you haven't done so already, install the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md#download). For further instructions, refer to the sample's `README` ([**C#** (archived)][], [**JavaScript** (archived)][], [**Java** (archived)][], or [**Python** (archived)][]). -Start the emulator, connect to your bot, and send a message as shown below. +Start the Emulator, connect to your bot, and send messages to your bot. The responses to your questions will vary, based on the information your knowledge base. -![test qna sample](../media/emulator-v4/qna-test-bot.png) +:::image type="content" source="../media/emulator-v4/qna-test-bot.png" alt-text="Test sample bot."::: ## Additional information -### Multi-turn prompts +The **QnA Maker multi-turn** sample ([**C#** multi-turn sample (archived)][], [**JavaScript** multi-turn sample (archived)][], [**Java** multi-turn sample (archived)][], [**Python** multi-turn sample (archived)][]) shows how to use a QnA Maker dialog to support QnA Maker's follow-up prompt and active learning features. -QnA Maker supports follow-up prompts, also known as multi-turn prompts. -If the QnA Maker knowledge base requires an additional response from the user, QnA Maker sends context information that you can use to prompt the user. This information is also used to make any follow-up calls to the QnA Maker service. +- QnA Maker supports follow-up prompts, also known as multi-turn prompts. +If the QnA Maker knowledge base requires more information from the user, QnA Maker sends context information that you can use to prompt the user. This information is also used to make any follow-up calls to the QnA Maker service. In version 4.6, the Bot Framework SDK added support for this feature. -To construct such a knowledge base, see the QnA Maker documentation on how to [Use follow-up prompts to create multiple turns of a conversation](https://aka.ms/qnamaker-multiturn-conversation). + To construct such a knowledge base, see the QnA Maker documentation on how to [Use follow-up prompts to create multiple turns of a conversation](/azure/ai-services/QnAMaker/How-To/multi-turn). - - ## Next steps -QnA Maker can be combined with other Cognitive Services, to make your bot even more powerful. The Dispatch tool provides a way to combine QnA with Language Understanding (LUIS) in your bot. +QnA Maker can be combined with other Azure AI services, to make your bot even more powerful. Bot Framework Orchestrator provides a way to combine QnA with Language Understanding (LUIS) in your bot. > [!div class="nextstepaction"] -> [Combine LUIS and QnA services using the Dispatch tool](./bot-builder-tutorial-dispatch.md) +> [Use Orchestrator for intent resolution](./bot-builder-tutorial-orchestrator.md) + +[**C#** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/csharp_dotnetcore/11.qnamaker +[**JavaScript** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/javascript_nodejs/11.qnamaker +[**Java** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/java_springboot/11.qnamaker +[**Python** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/python/11.qnamaker + +[**C#** multi-turn sample (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/csharp_dotnetcore/49.qnamaker-all-features +[**JavaScript** multi-turn sample (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/javascript_nodejs/49.qnamaker-all-features +[**Java** multi-turn sample (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/java_springboot/49.qnamaker-all-features +[**Python** multi-turn sample (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/python/49.qnamaker-all-features diff --git a/articles/v4sdk/bot-builder-howto-send-messages.md b/articles/v4sdk/bot-builder-howto-send-messages.md index 126867eb5..8520efde8 100644 --- a/articles/v4sdk/bot-builder-howto-send-messages.md +++ b/articles/v4sdk/bot-builder-howto-send-messages.md @@ -1,29 +1,33 @@ --- -title: Send and receive text message - Bot Service -description: Learn about how to send and receive text messages within the Bot Framework SDK. -keywords: sending message, message activities, simple text message, message, text message, receive message -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 +title: Send and receive text messages +description: Learn how to make bots send and receive text messages. +keywords: sending message, message activities, simple text message, message, text message, receive message +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- -# Send and receive text message +# Send and receive text messages -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -The primary way your bot will communicate with users, and likewise receive communication, is through **message** activities. Some messages may simply consist of plain text, while others may contain richer content such as cards or attachments. Your bot's turn handler receives messages from the user, and you can send responses to the user from there. The turn context object provides methods for sending messages back to the user. This article describes how to send simple text messages. +The primary way your bot will communicate with users, and likewise receive communication, is through **message** activities. Some messages may only contain plain text, while others may contain richer content such as cards or attachments. Your bot's turn handler receives messages from the user, and you can send responses to the user from there. The turn context object provides methods for sending messages back to the user. This article describes how to send plain text messages. Markdown is supported for most text fields, but support may vary by channel. -For a running bot sending and receiving messages, follow the quickstarts at the top of the table of contents or check out the [article on how bots work](bot-builder-basics.md#bot-structure), which also links to simple samples available for you to run yourself. +For a running bot sending and receiving messages, follow the quickstarts at the top of the table of contents or check out the [article on how bots work](bot-builder-basics.md#bot-application-structure), which also links to samples available for you to run yourself. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Send a text message -To send a simple text message, specify the string you want to send as the activity: +To send a text message, specify the string you want to send as the activity: # [C#](#tab/csharp) @@ -41,6 +45,14 @@ In the bot's activity handlers, use the turn context object's `sendActivity` met await context.sendActivity("Welcome!"); ``` +# [Java](#tab/java) + +In the bot's activity handlers, use the turn context object's `sendActivity` method to send a single message response. You can also use the object's `sendActivities` method to send multiple responses at once. + +```java +turnContext.sendActivity("Welcome!"); +``` + # [Python](#tab/python) In the bot's activity handlers, use the turn context object's `send_activity` method to send a single message response. @@ -49,14 +61,28 @@ In the bot's activity handlers, use the turn context object's `send_activity` me await turn_context.send_activity("Welcome!") ``` +# [LG](#tab/lg) + +**Language Generation** (LG) provides templates that include one or more variations of text that are used for composition and expansion. +One of the variations provided will be picked at random by the LG system. + +The following example shows a bot response template that includes two variations. + +```lg +# GreetingPrefix +- Hi +- Hello +``` + --- + ## Receive a text message -To receive a simple text message, use the *text* property of the *activity* object. +To handle a text message, use the *text* property of the *activity* object. # [C#](#tab/csharp) -In the bot's activity handlers, use the following code to receive a message. +In the bot's activity handlers, use the following code to receive a message. ```cs var responseMessage = turnContext.Activity.Text; @@ -70,6 +96,14 @@ In the bot's activity handlers, use the following code to receive a message. let text = turnContext.activity.text; ``` +# [Java](#tab/java) + +In the bot's activity handlers, use the following code to receive a message. + +```java +String responseMessage = turnContext.getActivity().getText(); +``` + # [Python](#tab/python) In the bot's activity handlers, use the following code to receive a message. @@ -78,12 +112,22 @@ In the bot's activity handlers, use the following code to receive a message. response = context.activity.text ``` +# [LG](#tab/lg) + +Add the following LG template to your .lg file to receive a message. + +```lg +# EchoMessage +You said '${turn.activity.text}' +``` + --- ## Send a typing indicator + Users expect a timely response to their messages. If your bot performs some long-running task like calling a server or executing a query without giving the user some indication that the bot heard them, the user could get impatient and send additional messages or just assume the bot is broken. -Web Chat and Direct Line channel bots can support the sending of a typing indication to show the user that the message was received and is being processed. Be aware that your bot needs to let the turn end within 15 seconds or the Connector service will timeout. For longer processes read more about sending [proactive messages](bot-builder-howto-proactive-message.md). +Web Chat and Direct Line channel bots can support the sending of a typing indication to show the user that the message was received and is being processed. However, your bot needs to let the turn end within 15 seconds or the Connector service will time out. For longer processes, read more about sending [proactive messages](bot-builder-howto-proactive-message.md). The following example demonstrates how to send a typing indication. @@ -114,19 +158,38 @@ protected override async Task OnMessageActivityAsync(ITurnContext { - if (context.activity.text === 'wait') { - await context.sendActivities([ - { type: ActivityTypes.Typing }, - { type: 'delay', value: 3000 }, - { type: ActivityTypes.Message, text: 'Finished typing' } - ]); - } else { - await context.sendActivity(`You said '${ context.activity.text }'. Say "wait" to watch me type.`); - } - await next(); + if (context.activity.text === 'wait') { + await context.sendActivities([ + { type: ActivityTypes.Typing }, + { type: 'delay', value: 3000 }, + { type: ActivityTypes.Message, text: 'Finished typing' } + ]); + } else { + await context.sendActivity(`You said '${ context.activity.text }'. Say "wait" to watch me type.`); + } + await next(); }); ``` +# [Java](#tab/java) + +```java +@Override +protected CompletableFuture onMessageActivity(TurnContext turnContext) { + if (turnContext.getActivity().getText().toLowerCase().equals("wait")) { + List activities = new ArrayList(); + activities.add(new Activity(ActivityTypes.TYPING)); + activities.add(new Activity(ActivityTypes.DELAY) {{setValue(3000);}}); + activities.add(MessageFactory.text("Finished typing", "Finished typing", null)); + return turnContext.sendActivities(activities).thenApply(result -> null); + } else { + + return turnContext.sendActivity(MessageFactory.text("Echo: " + turnContext.getActivity().getText())) + .thenApply(sendResult -> null); + } +} +``` + # [Python](#tab/python) ```python @@ -151,12 +214,22 @@ async def on_message_activity(self, turn_context: TurnContext): ) ``` +# [LG](#tab/lg) + +```lg +# TypingIndicator +[Activity + Type = typing +] +``` + --- ## Additional resources -- For more information about activity processing in general, see [activity processing](~/v4sdk/bot-builder-basics.md#the-activity-processing-stack). -- For more information on formatting, see the [message activity section](https://aka.ms/botSpecs-activitySchema#message-activity) of the Bot Framework Activity schema. +- [Activity processing](~/v4sdk/bot-builder-basics.md#the-activity-processing-stack) +- [Message activity section](https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-activity) +- [Language Generation](bot-builder-concept-language-generation.md) ## Next steps diff --git a/articles/v4sdk/bot-builder-howto-v4-luis.md b/articles/v4sdk/bot-builder-howto-v4-luis.md index 7b7b0376a..16504eb5f 100644 --- a/articles/v4sdk/bot-builder-howto-v4-luis.md +++ b/articles/v4sdk/bot-builder-howto-v4-luis.md @@ -1,41 +1,52 @@ --- -title: Add natural language understanding to your bot - Bot Service -description: Learn how to use LUIS for natural language understanding with the Bot Framework SDK. +title: Add natural language understanding to your bot +description: Learn how to use LUIS for natural language understanding in your bot. keywords: Language Understanding, LUIS, intent, recognizer, entities, middleware -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/24/2020 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Add natural language understanding to your bot -[!INCLUDE[applies-to](../includes/applies-to.md)] -The ability to understand what your user means conversationally and contextually can be a difficult task, but can provide your bot a more natural conversation feel. Language Understanding, called LUIS, enables you to do just that so that your bot can recognize the intent of user messages, allow for more natural language from your user, and better direct the conversation flow. This topic walks you through adding LUIS to a flight booking application to recognize different intents and entities contained within user input. +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + +The ability to understand what your user means conversationally and contextually can be a difficult task, but can provide your bot a more natural conversation feel. _Language Understanding (LUIS)_ is a cloud-based API service that enables you to do just that so that your bot can recognize the intent of user messages, allow for more natural language from your user, and better direct the conversation flow. + +This topic walks you through adding LUIS to a flight booking application to recognize different intents and entities contained within user input. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites -- [LUIS](https://www.luis.ai) account -- The code in this article is based on the **Core Bot** sample. You'll need a copy of the sample in **[C#](https://aka.ms/cs-core-sample)**, **[JavaScript](https://aka.ms/js-core-sample)**, or **[Python](https://aka.ms/python-core-sample)**. -- Knowledge of [bot basics](bot-builder-basics.md), [natural language processing](https://docs.microsoft.com/azure/cognitive-services/luis/what-is-luis), and [managing bot resources](bot-file-basics.md). +- A [LUIS](https://www.luis.ai) account. +- A copy of the **Core Bot** sample in [**C#**](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot), [**JavaScript**](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot), [**Java**](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/13.core-bot), or [**Python**](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/13.core-bot). +- Knowledge of [bot basics](bot-builder-basics.md) and [natural language processing](/azure/ai-services/luis/what-is-luis). ## About this sample -This core bot coding sample shows an example of an airport flight booking application. It uses a LUIS service to recognize the user input and return the top recognized LUIS intent. +This core bot sample shows an example of an airport flight booking application. It uses a LUIS service to recognize the user input and return the top recognized LUIS intent. + +The language model contains three intents: `Book Flight`, `Cancel`, and `None`. LUIS will use these intents to understand what the user meant when they send a message to the bot. The language model also defines entities that LUIS can extract from the user's input, such as the origin or destination airport. # [C#](#tab/csharp) -After each processing of user input, `DialogBot` saves the current state of both `UserState` and `ConversationState`. Once all the required information has been gathered the coding sample creates a demo flight booking reservation. In this article we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is shown below: +After each processing of user input, `DialogBot` saves the current state of both `UserState` and `ConversationState`. Once all the required information has been gathered, the coding sample creates a demo flight booking reservation. In this article, we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is: - `OnMembersAddedAsync` is called when a new user is connected and displays a welcome card. - `OnMessageActivityAsync` is called for each user input received. -![LUIS sample logic flow](./media/how-to-luis/luis-logic-flow.png) +:::image type="content" source="./media/how-to-luis/luis-logic-flow.png" alt-text="Class diagram outlining the structure of the C# sample."::: -The `OnMessageActivityAsync` module runs the appropriate dialog through the `Run` dialog extension method. Then the main dialog calls the LUIS helper to find the the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper fills out information from the user that LUIS returned. After that, the main dialog starts the `BookingDialog`, which acquires additional information as needed from the user such as: +The `OnMessageActivityAsync` module runs the appropriate dialog through the `Run` dialog extension method. Then the main dialog calls the LUIS helper to find the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper fills out information from the user that LUIS returned. After that, the main dialog starts the `BookingDialog`, which acquires additional information as needed from the user such as: - `Origin` the originating city - `TravelDate` the date to book the flight @@ -43,14 +54,14 @@ The `OnMessageActivityAsync` module runs the appropriate dialog through the `Run # [JavaScript](#tab/javascript) -After each processing of user input, `dialogBot` saves the current state of both `userState` and `conversationState`. Once all the required information has been gathered the coding sample creates a demo flight booking reservation. In this article we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is shown below: +After each processing of user input, `dialogBot` saves the current state of both `userState` and `conversationState`. Once all the required information has been gathered, the coding sample creates a demo flight booking reservation. In this article, we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is: - `onMembersAdded` is called when a new user is connected and displays a welcome card. - `OnMessage` is called for each user input received. -![LUIS sample javascript logic flow](./media/how-to-luis/luis-logic-flow-js.png) +:::image type="content" source="./media/how-to-luis/luis-logic-flow-js.png" alt-text="Class diagram outlining the structure of the JavaScript sample."::: -The `onMessage` module runs the `mainDialog` which gathers user input. +The `onMessage` module runs the `mainDialog`, which gathers user input. Then the main dialog calls the LUIS helper `FlightBookingRecognizer` to find the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper fills out information from the user that LUIS returned. Upon the response back, `mainDialog` preserves information for the user returned by LUIS and starts `bookingDialog`. `bookingDialog` acquires additional information as needed from the user such as @@ -58,16 +69,33 @@ Upon the response back, `mainDialog` preserves information for the user returned - `origin` the originating city. - `travelDate` the date to book the flight. +# [Java](#tab/java) + +1. After each processing of user input, `DialogBot` saves the current state of both `UserState` and `ConversationState`. +1. Once all the required information has been gathered, the coding sample creates a demo flight booking reservation. +1. In this article, we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is: + +- `onMembersAdded` is called when a new user is connected and displays a welcome card. +- `onMessageActivity` is called for each user input received. + +:::image type="content" source="./media/how-to-luis/luis-logic-flow-java.png" alt-text="Class diagram outlining the structure of the Java sample."::: + +The `onMessageActivity` module runs the appropriate dialog through the `run` dialog extension method. Then the main dialog calls the LUIS helper to find the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper fills out information from the user that LUIS returned. After that, the main dialog starts the `BookingDialog`, which acquires additional information as needed from the user such as: + +- `Origin` the originating city +- `TravelDate` the date to book the flight +- `Destination` the destination city + # [Python](#tab/python) -After each processing of user input, `DialogBot` saves the current state of both `user_state` and `conversation_state`. Once all the required information has been gathered the coding sample creates a demo flight booking reservation. In this article we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is shown below: +After each processing of user input, `DialogBot` saves the current state of both `user_state` and `conversation_state`. Once all the required information has been gathered, the coding sample creates a demo flight booking reservation. In this article, we'll be covering the LUIS aspects of this sample. However, the general flow of the sample is: - `on_members_added_activity` is called when a new user is connected and displays a welcome card. - `on_message_activity` is called for each user input received. -![LUIS sample Python logic flow](./media/how-to-luis/luis-logic-flow-python.png) +:::image type="content" source="./media/how-to-luis/luis-logic-flow-python.png" alt-text="Class diagram outlining the structure of the Python sample."::: -The `on_message_activity` module runs the appropriate dialog through the `run_dialog` dialog extension method. Then the main dialog calls `LuisHelper` to find the the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper function fills out information from the user that LUIS returned. After that, the main dialog starts the `BookingDialog`, which acquires additional information as needed from the user such as: +The `on_message_activity` module runs the appropriate dialog through the `run_dialog` dialog extension method. Then the main dialog calls `LuisHelper` to find the top scoring user intent. If the top intent for the user input returns "BookFlight", the helper function fills out information from the user that LUIS returned. After that, the main dialog starts the `BookingDialog`, which acquires additional information as needed from the user such as: - `destination` the destination city. - `origin` the originating city. @@ -75,60 +103,75 @@ The `on_message_activity` module runs the appropriate dialog through the `run_di --- -For details on the other aspects of the sample like dialogs or state, see [Gather user input using a dialog prompt](bot-builder-prompts.md) or [Save user and conversation data](bot-builder-howto-v4-state.md). +This article covers how to add LUIS to a bot. For information about using dialogs or state, see how to [gather user input using a dialog prompt](bot-builder-prompts.md) or [save user and conversation data](bot-builder-howto-v4-state.md), respectively. ## Create a LUIS app in the LUIS portal -Sign in to the LUIS portal to create your own version of the sample LUIS app. You can create and manage your applications on **My Apps**. - -1. Select **Import new app**. -1. Click **Choose App file (JSON format)...** -1. Select `FlightBooking.json` file located in the `CognitiveModels` folder of the sample. In the **Optional Name**, enter **FlightBooking**. This file contains three intents: 'Book Flight', 'Cancel', and 'None'. We'll use these intents to understand what the user meant when they send a message to the bot. -1. [Train](https://docs.microsoft.com/azure/cognitive-services/LUIS/luis-how-to-train) the app. -1. [Publish](https://docs.microsoft.com/azure/cognitive-services/LUIS/publishapp) the app to *production* environment. +1. [Sign in to the LUIS portal][sign-in-luis-portal] and if needed [create an account][create-account] and [authoring resource][create-authoring-resource]. +1. On the **Conversation apps** page in [LUIS][conversation-apps], select **Import**, then **Import as JSON**. +1. In the **Import new app** dialog: + 1. Choose the **FlightBooking.json** file in the **CognitiveModels** folder of the sample. + 1. Enter `FlightBooking` as the optional name of the app, and select **Done**. +1. The site may display **How to create an effective LUIS app** and **Upgrade your composite entities** dialogs. You can dismiss these dialogs and continue. +1. Train your app, then publish your app to the _production_ environment. + For more information, see the LUIS documentation on how to [train](/azure/ai-services/LUIS/how-to/train-test) and [publish](/azure/ai-services/LUIS/how-to/publish) an app. ### Why use entities -LUIS entities allow your bot to intelligently understand certain things or events that are different than the standard intents. This enables you to gather extra information from the user, which lets your bot respond more intelligently or possibly skip certain questions where it asks the user for that information. Along with definitions for the three LUIS intents 'Book Flight', 'Cancel', and 'None' the FlightBooking.json file also contains a set of entities such as 'From.Airport' and 'To.Airport'. These entities allow LUIS to detect and return additional information contained within the user's original input when they request a new travel booking. - -For information on how entity information appears in a LUIS result, see [Extract data from utterance text with intents and entities](https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-data-extraction). +LUIS entities enable your bot to understand events beyond standard intents. This enables you to gather from users additional information, so your bot can ask questions and respond more intelligently. Along with definitions for the three LUIS intents 'Book Flight', 'Cancel', and 'None', the FlightBooking.json file also contains a set of entities such as 'From.Airport' and 'To.Airport'. These entities allow LUIS to detect and return additional information contained within the user's original input when they request a new travel booking. ## Obtain values to connect to your LUIS app -Once your LUIS app is published, you can access it from your bot. You will need to record several values to access your LUIS app from within your bot. You can retrieve that information using the LUIS portal. +Once your LUIS app is published, you can access it from your bot. You'll need to record several values to access your LUIS app from within your bot. You can retrieve that information using the LUIS portal. ### Retrieve application information from the LUIS.ai portal -The settings file (`appsettings.json` or `.env`) acts as the place to bring all service references together in one place. The information you retrieve will be added to this file in the next section. +The settings file (`appsettings.json`, `.env` or `config.py`) acts as the place to bring all service references together in one place. The information you retrieve will be added to this file in the next section. 1. Select your published LUIS app from [luis.ai](https://www.luis.ai). 1. With your published LUIS app open, select the **MANAGE** tab. -![Manage LUIS app](./media/how-to-luis/manage-luis-app.png) -1. Select the **Application Information** tab on the left side, record the value shown for _Application ID_ as . -1. Select the **Keys and Endpoints** tab on the left side, record the value shown for _Authoring Key_ as . -1. Scroll down to the end of the page, record the value shown for _Region_ as . +1. Select the **Settings** tab on the left side and record the value shown for _Application ID_ as \. + + :::image type="content" source="./media/how-to-luis/manage-luis-app-app-info.png" alt-text="Screenshot of the Manage page displaying your application ID." lightbox="./media/how-to-luis/manage-luis-app-app-info.png"::: + +1. Select **Azure Resources**, then **Prediction Resource**. Record the value shown for _Location_ as \ and _Primary Key_ as \. + + :::image type="content" source="./media/how-to-luis/manage-luis-app-azure-resources.png" alt-text="Screenshot of the Manage page displaying your location and primary key." lightbox="./media/how-to-luis/manage-luis-app-azure-resources.png"::: + + Alternatively, you can use the region and primary key for your authoring resource. ### Update the settings file # [C#](#tab/csharp) -Add the information required to access your LUIS app including application id, authoring key, and region into the `appsettings.json` file. These are the values you saved previously from your published LUIS app. Note that the API host name should be in the format `.api.cognitive.microsoft.com`. +Add the information required to access your LUIS app including application ID, authoring key, and region into the `appsettings.json` file. In the previous step, you retrieved these values from your published LUIS app. The API host name should be in the format `.api.cognitive.microsoft.com`. + +**appsetting.json** -**appsetting.json** -[!code-json[appsettings](~/../BotBuilder-Samples/samples/csharp_dotnetcore/13.core-bot/appsettings.json?range=1-7)] +[!code-json[appsettings](~/../BotBuilder-Samples/samples/csharp_dotnetcore/13.core-bot/appsettings.json)] # [JavaScript](#tab/javascript) -Add the information required to access your LUIS app including application id, authoring key, and region into the `.env` file. These are the values you saved previously from your published LUIS app. Note that the API host name should be in the format `.api.cognitive.microsoft.com`. +Add the information required to access your LUIS app including application ID, authoring key, and region into the `.env` file. In the previous step, you retrieved these values from your published LUIS app. The API host name should be in the format `.api.cognitive.microsoft.com`. + +**.env** + +[!code-ini[.env file](~/../BotBuilder-Samples/samples/javascript_nodejs/13.core-bot/.env)] + +# [Java](#tab/java) -**.env** -[!code[env](~/../BotBuilder-Samples/samples/javascript_nodejs/13.core-bot/.env?range=1-5)] +Add the information required to access your LUIS app including application ID, authoring key, and region into the `application.properties` file. In the previous step, you retrieved these values from your published LUIS app. The API host name should be in the format `.api.cognitive.microsoft.com`. + +**application.properties** + +[!code-ini[appsettings](~/../BotBuilder-Samples/samples/java_springboot/13.core-bot/src/main/resources/application.properties)] # [Python](#tab/python) -Add the information required to access your LUIS app including application id, authoring key, and region into the `config.py` file. These are the values you saved previously from your published LUIS app. Note that the API host name should be in the format `.api.cognitive.microsoft.com`. +Add the information required to access your LUIS app including application ID, authoring key, and region into the `config.py` file. In the previous step, you retrieved these values from your published LUIS app. The API host name should be in the format `.api.cognitive.microsoft.com`. **config.py** + [!code-python[config.py](~/../botbuilder-samples/samples/python/13.core-bot/config.py?range=14-19)] --- @@ -139,44 +182,65 @@ Add the information required to access your LUIS app including application id, a Be sure that the **Microsoft.Bot.Builder.AI.Luis** NuGet package is installed for your project. -To connect to the LUIS service, the bot pulls the information you added above from the appsetting.json file. The `FlightBookingRecognizer` class contains code with your settings from the appsetting.json file and queries the LUIS service by calling `RecognizeAsync` method. +To connect to the LUIS service, the bot pulls the information you added to the appsetting.json file. The `FlightBookingRecognizer` class contains code with your settings from the appsetting.json file and queries the LUIS service by calling `RecognizeAsync` method. **FlightBookingRecognizer.cs** [!code-csharp[luisHelper](~/../BotBuilder-Samples/samples/csharp_dotnetcore/13.core-bot/FlightBookingRecognizer.cs?range=12-48)] -The `FlightBookingEx.cs` contains the logic to extract *From*, *To* and *TravelDate*; it extends the partial class `FlightBooking.cs` used to store LUIS results when calling `FlightBookingRecognizer.RecognizeAsync` from the `MainDialog.cs`. +The `FlightBookingEx.cs` contains the logic to extract _From_, _To_ and _TravelDate_; it extends the partial class `FlightBooking.cs` used to store LUIS results when calling `FlightBookingRecognizer.RecognizeAsync` from the `MainDialog.cs`. **CognitiveModels\FlightBookingEx.cs** -[!code-csharp[luis helper](~/../BotBuilder-Samples/samples/csharp_dotnetcore/13.core-bot/CognitiveModels/FlightBookingEx.cs?range=8-35)] +[!code-csharp[LUIS helper](~/../BotBuilder-Samples/samples/csharp_dotnetcore/13.core-bot/CognitiveModels/FlightBookingEx.cs?range=8-35)] # [JavaScript](#tab/javascript) To use LUIS, your project needs to install the **botbuilder-ai** npm package. -To connect to the LUIS service, the bot uses the information you added above from the `.env` file. The `flightBookingRecognizer.js` class contains the code that imports your settings from the `.env` file and queries the LUIS service by calling `recognize()` method. +To connect to the LUIS service, the bot uses the information you added to the `.env` file. The `flightBookingRecognizer.js` class contains the code that imports your settings from the `.env` file and queries the LUIS service by calling `recognize()` method. **dialogs/flightBookingRecognizer.js** -[!code-javascript[luis helper](~/../BotBuilder-Samples/samples/javascript_nodejs/13.core-bot/dialogs/flightBookingRecognizer.js?range=6-70)] +[!code-javascript[LUIS helper](~/../BotBuilder-Samples/samples/javascript_nodejs/13.core-bot/dialogs/flightBookingRecognizer.js?range=6-70)] The logic to extract From, To and TravelDate is implemented as helper methods inside `flightBookingRecognizer.js`. These methods are used after calling `flightBookingRecognizer.executeLuisQuery()` from `mainDialog.js` +# [Java](#tab/java) + +Be sure that the **com.microsoft.bot.bot-ai-luis-v3** package is added to your pom.xml file. + +:::code language="xml" source="~/../BotBuilder-Samples/samples/java_springboot/13.core-bot/pom.xml" range="109-113"::: + +To connect to the LUIS service, the bot pulls the information you added to the application.properties file. The `FlightBookingRecognizer` class contains code with your settings from the application.properties file and queries the LUIS service by calling `recognize` method. + +**FlightBookingRecognizer.java** + +[!code-java[luisHelper](~/../BotBuilder-Samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/FlightBookingRecognizer.java?range=27-50)] + +[!code-java[luisHelper](~/../BotBuilder-Samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/FlightBookingRecognizer.java?range=142-151)] + +The `FlightBookingRecognizer.cs` contains the logic to extract _From_, _To_ and _TravelDate_; and is called from the `MainDialog.java` to decode the results of the Luis query result. + +**FlightBookingRecognizer.java** + +[!code-csharp[LUIS helper](~/../BotBuilder-Samples/samples/java_springboot/13.core-bot/src/main/java/com/microsoft/bot/sample/core/FlightBookingRecognizer.java?range=71-140)] + # [Python](#tab/python) Be sure that the **botbuilder-ai** PyPI package is installed for your project. -To connect to the LUIS service, the bot uses the information you added above from the `config.py` file. The `FlightBookingRecognizer` class contains the code that imports your settings from the `config.py` file and queries the LUIS service by calling `recognize()` method. +To connect to the LUIS service, the bot uses the information you added to the `config.py` file. The `FlightBookingRecognizer` class contains the code that imports your settings from the `config.py` file and queries the LUIS service by calling `recognize()` method. **flight_booking_recognizer.py** -[!code-python[config.py](~/../botbuilder-samples/samples/python/13.core-bot/flight_booking_recognizer.py?range=10-34)] +[!code-python[config.py](~/../botbuilder-samples/samples/python/13.core-bot/flight_booking_recognizer.py?range=10-36&highlight=26)] -The logic to extract *From*, *To* and *travel_date* is implemented as helper methods from the `LuisHelper` class inside `luis_helper.py`. These methods are used after calling `LuisHelper.execute_luis_query()` from `main_dialog.py` +The logic to extract _From_, _To_ and _travel_date_ is implemented as helper methods from the `LuisHelper` class inside `luis_helper.py`. These methods are used after calling `LuisHelper.execute_luis_query()` from `main_dialog.py` **helpers/luis_helper.py** -[!code-python[luis helper](~/../botbuilder-samples/samples/python/13.core-bot/helpers/luis_helper.py?range=30-102)] + +[!code-python[LUIS helper](~/../botbuilder-samples/samples/python/13.core-bot/helpers/luis_helper.py?range=30-102)] --- @@ -184,21 +248,31 @@ LUIS is now configured and connected for your bot. ## Test the bot -Download and install the latest [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) +Download and install the latest [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) + +1. Run the sample locally on your machine. If you need instructions, refer to the `README` file for the [C# Sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot), [JS Sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot) or [Python Sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/13.core-bot). -1. Run the sample locally on your machine. If you need instructions, refer to the readme file for the [C# Sample](https://aka.ms/cs-core-sample), [JS Sample](https://aka.ms/js-core-sample) or [Python Sample](https://aka.ms/python-core-sample). +1. In the Emulator, type a message such as "travel to paris" or "going from paris to berlin". Use any utterance found in the file FlightBooking.json for training the intent "Book flight". -1. In the emulator, type a message such as "travel to paris" or "going from paris to berlin". Use any utterance found in the file FlightBooking.json for training the intent "Book flight". +If the top intent returned from LUIS resolves to "Book flight", your bot will ask more questions until it has enough information stored to create a travel booking. At that point it will return this booking information back to your user. -![LUIS booking input](./media/how-to-luis/luis-user-travel-input.png) +At this point, the code bot logic will reset and you can continue to create more bookings. -If the top intent returned from LUIS resolves to "Book flight" your bot will ask additional questions until it has enough information stored to create a travel booking. At that point it will return this booking information back to your user. +## Additional information -![LUIS booking result](./media/how-to-luis/luis-travel-result.png) +For more about LUIS, see the LUIS documentation: -At this point the code bot logic will reset and you can continue to create additional bookings. +- [What is Language Understanding (LUIS)?](/azure/ai-services/LUIS/what-is-luis) +- [Create a new LUIS app in the LUIS portal](/azure/ai-services/LUIS/how-to/sign-in) +- [Design with intent and entity models](/azure/ai-services/LUIS/luis-concept-model) +- [Migrate to V3 Authoring APIS](/azure/ai-services/luis/luis-migration-authoring-entities) +- [Migrate to V3 Prediction APIs](/azure/ai-services/luis/luis-migration-api-v3) -## Next steps +> [!TIP] +> Different parts of the SDK define separate _entity_ classes or elements. +> For message entities, see [Entities and activity types](../bot-service-activities-entities.md). -> [!div class="nextstepaction"] -> [Use QnA Maker to answer questions](./bot-builder-howto-qna.md) +[sign-in-luis-portal]: /azure/ai-services/luis/how-to/sign-in +[create-account]: https://azure.microsoft.com/products/ai-services/ +[conversation-apps]: https://www.luis.ai/applications +[create-authoring-resource]: /azure/ai-services/luis/luis-how-to-azure-subscription diff --git a/articles/v4sdk/bot-builder-howto-v4-state.md b/articles/v4sdk/bot-builder-howto-v4-state.md index 62fc971cf..2c21438d5 100644 --- a/articles/v4sdk/bot-builder-howto-v4-state.md +++ b/articles/v4sdk/bot-builder-howto-v4-state.md @@ -1,42 +1,54 @@ --- -title: Save user and conversation data - Bot Service -description: Learn how to save and retrieve state data with the Bot Framework SDK. -keywords: conversation state, user state, conversation, saving state, managing bot state -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/7/2020 +title: Save user and conversation data +description: Learn how the Bot Framework SDK manages user and conversation data (state). See how to set up storage for this data, read it, and write it. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Save user and conversation data -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] A bot is inherently stateless. Once your bot is deployed, it may not run in the same process or on the same machine from one turn to the next. However, your bot may need to track the context of a conversation so that it can manage its behavior and remember answers to previous questions. The state and storage features of the Bot Framework SDK allow you to add state to your bot. Bots use state management and storage objects to manage and persist state. The state manager provides an abstraction layer that lets you access state properties using property accessors, independent of the type of underlying storage. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] + ## Prerequisites - Knowledge of [bot basics](bot-builder-basics.md) and how bots [manage state](bot-builder-concept-state.md) is required. -- The code in this article is based on the **State Management Bot sample**. You'll need a copy of the sample in either [CSharp](https://aka.ms/statebot-sample-cs), [JavaScript](https://aka.ms/statebot-sample-js) or [Python](https://aka.ms/bot-state-python-sample-code). +- The code in this article is based on the **State Management Bot sample**. You'll need a copy of the sample in either [C#][cs-sample], [JavaScript][js-sample], [Java][java-sample], or [Python][py-sample]. + +[cs-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/45.state-management +[js-sample]: https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/45.state-management +[java-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/45.state-management +[py-sample]: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/45.state-management ## About this sample -Upon receiving user input, this sample checks the stored conversation state to see if this user has previously been prompted to provide their name. If not, the user's name is requested and that input is stored within user state. If so, the name stored within user state is used to converse with the user and their input data, along with the time received and input channel Id, is returned back to the user. The time and channel Id values are retrieved from the user conversation data and then saved to conversation state. The following diagram shows the relationship between the bot, user profile, and conversation data classes. +Upon receiving user input, this sample checks the stored conversation state to see if this user has previously been prompted to provide their name. If not, the user's name is requested and that input is stored within user state. If so, the name stored within user state is used to converse with the user and their input data, along with the time received and input channel ID, is returned back to the user. The time and channel ID values are retrieved from the user conversation data and then saved to conversation state. The following diagram shows the relationship between the bot, user profile, and conversation data classes. ## [C#](#tab/csharp) -![state bot sample](media/StateBotSample-Overview.png) +:::image type="content" source="media/StateBotSample-Overview.png" alt-text="Class diagram outlining the structure of the C# sample."::: ## [JavaScript](#tab/javascript) -![state bot sample](media/StateBotSample-JS-Overview.png) +:::image type="content" source="media/StateBotSample-JS-Overview.png" alt-text="Class diagram outlining the structure of the JavaScript sample."::: + +## [Java](#tab/java) + +:::image type="content" source="media/StateBotSample-Overview.png" alt-text="Class diagram outlining the structure of the Java sample."::: ## [Python](#tab/python) -![state bot sample](media/StateBotSample-Python-Overview.png) +:::image type="content" source="media/StateBotSample-Python-Overview.png" alt-text="Class diagram outlining the structure of the Python sample."::: --- @@ -46,34 +58,55 @@ Upon receiving user input, this sample checks the stored conversation state to s The first step in setting up state management is to define the classes containing the information to manage in the user and conversation state. The example used in this article, defines the following classes: -- In **UserProfile.cs**, we define a `UserProfile` class for the user information that the bot will collect. -- In **ConversationData.cs**, we define a `ConversationData` class to control our conversation state while gathering user information. +- In **UserProfile.cs**, you define a `UserProfile` class for the user information that the bot will collect. +- In **ConversationData.cs**, you define a `ConversationData` class to control our conversation state while gathering user information. The following code examples show the definitions for the `UserProfile` and `ConversationData` classes. -**UserProfile.cs** -[!code-csharp[UserProfile](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/UserProfile.cs?range=7-11)] +**UserProfile.cs** -**ConversationData.cs** -[!code-csharp[ConversationData](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/ConversationData.cs?range=6-17)] +[!code-csharp[UserProfile class](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/UserProfile.cs?range=7-10)] + +**ConversationData.cs** + +[!code-csharp[ConversationData class](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/ConversationData.cs?range=7-17)] ## [JavaScript](#tab/javascript) -This step is not necessary in JavaScript. +This step isn't necessary in JavaScript. + +## [Java](#tab/java) + +The first step in setting up state management is to define the classes containing the information to manage in the user and conversation state. The example used in this article defines the following classes: + +- In **UserProfile.java**, you define a `UserProfile` class for the user information that the bot will collect. +- In **ConversationData.java**, you define a `ConversationData` class to control our conversation state while gathering user information. + +The following code examples show the definitions for the `UserProfile` and `ConversationData` classes. + +**UserProfile.java** + +[!code-java[UserProfile class](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/UserProfile.java?range=18-28)] + +**ConversationData.java** + +[!code-java[ConversationData class](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/ConversationData.java?range=18-46)] ## [Python](#tab/python) The first step in setting up state management is to define the classes containing the information to manage in the user and conversation state. The example used in this article, defines the following classes: -- The **user_profile.py** contains the `UserProfile` class which stores the user information collected by the bot. -- The **conversation_data.py** contains the `ConversationData` class which controls the conversation state while gathering user information. +- The **user_profile.py** contains the `UserProfile` class that stores the user information collected by the bot. +- The **conversation_data.py** contains the `ConversationData` class that controls the conversation state while gathering user information. The following code examples show the definitions for the `UserProfile` and `ConversationData` classes. **user_profile.py** + [!code-python[user_profile](~/../botbuilder-samples/samples/python/45.state-management/data_models/user_profile.py?range=5-7)] **conversation_data.py** + [!code-python[conversation_data](~/../botbuilder-samples/samples/python/45.state-management/data_models/conversation_data.py?range=5-14)] --- @@ -82,35 +115,47 @@ The following code examples show the definitions for the `UserProfile` and `Conv ## [C#](#tab/csharp) -Next, we register `MemoryStorage` that is used to create `UserState` and `ConversationState` objects. The user and conversation state objects are created at `Startup` and dependency injected into the bot constructor. Other services for a bot that are registered are: a credential provider, an adapter, and the bot implementation. +Next, you register `MemoryStorage` that is used to create `UserState` and `ConversationState` objects. The user and conversation state objects are created at `Startup` and dependency injected into the bot constructor. Other services for a bot that are registered are: a credential provider, an adapter, and the bot implementation. + +**Startup.cs** -**Startup.cs** -[!code-csharp[ConfigureServices](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/Startup.cs?range=26-29)] -[!code-csharp[ConfigureServices](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/Startup.cs?range=51-57)] +[!code-csharp[MemoryStorage, UserState, and ConversationState definitions](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/Startup.cs?range=34-35,55-61)] **Bots/StateManagementBot.cs** + [!code-csharp[Bot constructor](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/Bots/StateManagementBot.cs?range=15-22)] ## [JavaScript](#tab/javascript) -Next, we register `MemoryStorage` that is then used to create `UserState` and `ConversationState` objects. These are created in **index.js** and consumed when the bot is created. +Next, you register `MemoryStorage` that is then used to create `UserState` and `ConversationState` objects. These are created in **index.js** and consumed when the bot is created. + +**index.js** + +[!code-javascript[MemoryStorage, UserState, ConversationState, and bot objects](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/index.js?range=43-50)] + +**bots/stateManagementBot.js** + +[!code-javascript[state properties and constructor](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=7-19)] + +## [Java](#tab/java) -**index.js** -[!code-javascript[index.js](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/index.js?range=33-39)] +Next, you register the `StateManagementBot` in Application.java. Both ConversationState and UserState are provided by default from the BotDependencyConfiguration class, and Spring will inject them into the getBot method. -**bots/stateManagementBot.js** -[!code-javascript[bot constructor](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=10-12)] -[!code-javascript[bot constructor](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=17-19)] +**Application.java** + +[!code-java[getBot method](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/Application.java?range=54-60)] ## [Python](#tab/python) -Next, we register `MemoryStorage` that is used to create `UserState` and `ConversationState` objects. These are created in **app.py** and consumed when the bot is created. +Next, you register `MemoryStorage` that is used to create `UserState` and `ConversationState` objects. These are created in **app.py** and consumed when the bot is created. **app.py** -[!code-python[app.py](~/../botbuilder-samples/samples/python/45.state-management/app.py?range=67-70)] + +[!code-python[MemoryStorage, UserState, ConversationState, and bot objects](~/../botbuilder-samples/samples/python/45.state-management/app.py?range=68-74)] **bots/state_management_bot.py** -[!code-python[bot constructor](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=13-25)] + +[!code-python[bot constructor](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=14-30)] --- @@ -118,97 +163,129 @@ Next, we register `MemoryStorage` that is used to create `UserState` and `Conver ## [C#](#tab/csharp) -Now we create property accessors using the `CreateProperty` method that provides a handle to the `BotState` object. Each state property accessor allows you to get or set the value of the associated state property. Before we use our state properties, we use each accessor to load the property from storage and get it from the state cache. To get the properly scoped key associated with the state property, we call the `GetAsync` method. +Now you create property accessors using the `CreateProperty` method that provides a handle to the `BotState` object. Each state property accessor allows you to get or set the value of the associated state property. Before you use the state properties, use each accessor to load the property from storage and get it from the state cache. To get the properly scoped key associated with the state property, you call the `GetAsync` method. **Bots/StateManagementBot.cs** + [!code-csharp[Create accessors](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/Bots/StateManagementBot.cs?range=42,45)] ## [JavaScript](#tab/javascript) -Now we create property accessors for `UserState` and `ConversationState`. Each state property accessor allows you to get or set the value of the associated state property. We use each accessor to load the associated property from storage and retrieve its current state from cache. +Now you create property accessors for `UserState` and `ConversationState`. Each state property accessor allows you to get or set the value of the associated state property. You use each accessor to load the associated property from storage and retrieve its current state from cache. **bots/stateManagementBot.js** + [!code-javascript[Create accessors](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=13-15)] +## [Java](#tab/java) + +Now you create property accessors using the `createProperty` method. Each state property accessor allows you to get or set the value of the associated state property. Before you use the state properties, use each accessor to load the property from storage and get it from the state cache. To get the properly scoped key associated with the state property, you call the `get` method. + +**StateManagementBot.java** + +[!code-java[Create property accessors](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/StateManagementBot.java?range=105-108)] + ## [Python](#tab/python) -Now we create property accessors for `UserProfile` and `ConversationData`. Each state property accessor allows you to get or set the value of the associated state property. We use each accessor to load the associated property from storage and retrieve its current state from cache. +Now you create property accessors for `UserProfile` and `ConversationData`. Each state property accessor allows you to get or set the value of the associated state property. You use each accessor to load the associated property from storage and retrieve its current state from cache. **bots/state_management_bot.py** + [!code-python[Create accessors](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=27-30)] --- ## Access state from your bot -The preceding section covers the initialization-time steps to add state property accessors to our bot. Now, we can use those accessors at run-time to read and write state information. The sample code below uses the following logic flow: +The preceding section covers the initialization-time steps to add state property accessors to our bot. Now, you can use those accessors at run-time to read and write state information. The sample code below uses the following logic flow: ## [C#](#tab/csharp) -- If userProfile.Name is empty and conversationData.PromptedUserForName is _true_, we retrieve the user name provided and store this within user state. -- If userProfile.Name is empty and conversationData.PromptedUserForName is _false_, we ask for the user's name. -- If userProfile.Name was previously stored, we retrieve message time and channel Id from the user input, echo all data back to the user, and store the retrieved data within conversation state. +- If `userProfile.Name` is empty and `conversationData.PromptedUserForName` is _true_, you retrieve the user name provided and store this within user state. +- If `userProfile.Name` is empty and `conversationData.PromptedUserForName` is _false_, you ask for the user's name. +- If `userProfile.Name` was previously stored, you retrieve message time and channel ID from the user input, echo all data back to the user, and store the retrieved data within conversation state. **Bots/StateManagementBot.cs** + [!code-csharp[OnMessageActivityAsync](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/bots/StateManagementBot.cs?range=38-85)] -Before we exit the turn handler, we use the state management objects' _SaveChangesAsync()_ method to write all state changes back to storage. +Before you exit the turn handler, you use the state management objects' _SaveChangesAsync()_ method to write all state changes back to storage. **Bots/StateManagementBot.cs** + [!code-csharp[OnTurnAsync](~/../BotBuilder-Samples/samples/csharp_dotnetcore/45.state-management/bots/StateManagementBot.cs?range=24-31)] ## [JavaScript](#tab/javascript) -- If userProfile.Name is empty and conversationData.PromptedUserForName is _true_, we retrieve the user name provided and store this within user state. -- If userProfile.Name is empty and conversationData.PromptedUserForName is _false_, we ask for the user's name. -- If userProfile.Name was previously stored, we retrieve message time and channel Id from the user input, echo all data back to the user, and store the retrieved data within conversation state. +- If `userProfile.Name` is empty and `conversationData.PromptedUserForName` is _true_, you retrieve the user name provided and store this within user state. +- If `userProfile.Name` is empty and `conversationData.PromptedUserForName` is _false_, you ask for the user's name. +- If `userProfile.Name` was previously stored, you retrieve message time and channel ID from the user input, echo all data back to the user, and store the retrieved data within conversation state. **bots/stateManagementBot.js** + [!code-javascript[OnMessage](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=21-58)] -Before we exit each dialog turn, we use the state management objects' _saveChanges()_ method to persist all changes by writing state back out to storage. +Before you exit each dialog turn, you use the state management objects' _saveChanges()_ method to persist all changes by writing state back out to storage. **bots/stateManagementBot.js** + [!code-javascript[OnDialog](~/../BotBuilder-Samples/samples/javascript_nodejs/45.state-management/bots/stateManagementBot.js?range=72-81)] -## [Python](#tab/python) +## [Java](#tab/java) -- If `user_profile.name` is empty and `conversation_data.prompted_for_user_name` is *true*, the bot retrieves the name provided by the user and stores it in the user's state. -- If `user_profile.name` is empty and `conversation_data.prompted_for_user_name` is *false*,the bot asks for the user's name. -- If `user_profile.name` was previously stored, the bot retrieves **message time** and **channel Id** from the user input, echoes the data back to the user, and stores the retrieved data in the conversation state. +- If `userProfile.getName()` is empty and `conversationData.getPromptedUserForName()` is _true_, you retrieve the user name provided and store this within user state. +- If `userProfile.getName()` is empty and `conversationData.getPromptedUserForName()` is _false_, you ask for the user's name. +- If `userProfile.getName()` was previously stored, you retrieve message time and channel ID from the user input, echo all data back to the user, and store the retrieved data within conversation state. -**bots/state_management_bot.py** -[!code-python[state_message_activity](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=47-89)] +**StateManagementBot.java** + +[!code-java[onMessageActivity method](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/StateManagementBot.java?range=102-168)] + +Before you exit the turn handler, you use the state management objects' _saveChanges()_ method to write all state changes back to storage. + +**StateManagementBot.java** -Before exiting each dialog turn, the bot uses the state management objects' `save_changes` method to persist all changes by writing state information in the storage. +[!code-java[onTurn method](~/../BotBuilder-Samples/samples/java_springboot/45.state-management/src/main/java/com/microsoft/bot/sample/statemanagement/StateManagementBot.java?range=58-64)] + +## [Python](#tab/python) + +- If `user_profile.name` is empty and `conversation_data.prompted_for_user_name` is _true_, the bot retrieves the name provided by the user and stores it in the user's state. +- If `user_profile.name` is empty and `conversation_data.prompted_for_user_name` is _false_, the bot asks for the user's name. +- If `user_profile.name` was previously stored, the bot retrieves message time and channel ID from the user input, echoes the data back to the user, and stores the retrieved data in the conversation state. **bots/state_management_bot.py** -[!code-python[state_storage](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=32-36)] ---- +[!code-python[on_message_activity](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=47-89)] -## Test the bot +Before each dialog turn ends, the bot uses the state management objects' `save_changes` method to persist all changes by writing state information in the storage. -Download and install the latest [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) +**bots/state_management_bot.py** -1. Run the sample locally on your machine. If you need instructions, refer to the README file for [C# Sample](https://aka.ms/statebot-sample-cs) or [JS Sample](https://aka.ms/statebot-sample-js). -1. Use the emulator to test the bot as shown below. +[!code-python[on_turn](~/../botbuilder-samples/samples/python/45.state-management/bots/state_management_bot.py?range=32-36)] -![test state bot sample](media/state-bot-testing-emulator.png) +--- -## Additional resources +## Test your bot -**Privacy:** If you intend to store user's personal data, you should ensure compliance with [General Data Protection Regulation](https://blog.botframework.com/2018/04/23/general-data-protection-regulation-gdpr). +1. Download and install the latest [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) +1. Run the sample locally on your machine. + If you need instructions, refer to the README for [C#][cs-sample], [JavaScript][js-sample], [Java][java-sample], or [Python][py-sample]. +1. Use the Emulator to test your sample bot. -**State management:** All of the state management calls are asynchronous, and last-writer-wins by default. In practice, you should get, set, and save state as close together in your bot as possible. +## Additional information -**Critical business data:** Use bot state to store preferences, user name, or the last thing they ordered, but do not use it to store critical business data. For critical data, [create your own storage components](bot-builder-custom-storage.md) or write directly to [storage](bot-builder-howto-v4-storage.md). +This article described how you can add state to your bot. See the following table for more information about related topics. -**Recognizer-Text:** The sample uses the Microsoft/Recognizers-Text libraries to parse and validate user input. For more information, see the [overview](https://github.com/Microsoft/Recognizers-Text#microsoft-recognizers-text-overview) page. +| Topic | Notes | +|:-|:-| +| Privacy | If you intend to store user's personal data, you should ensure compliance with [General Data Protection Regulation](https://blog.botframework.com/2018/04/23/general-data-protection-regulation-gdpr). | +| State management | All of the state management calls are asynchronous, and last-writer-wins by default. In practice, you should get, set, and save state as close together in your bot as possible. For a discussion of how to implement optimistic locking, see [Implement custom storage for your bot](bot-builder-custom-storage.md). | +| Critical business data | Use bot state to store preferences, user name, or the last thing they ordered, but don't use it to store critical business data. For critical data, [create your own storage components](bot-builder-custom-storage.md) or write directly to [storage](bot-builder-howto-v4-storage.md). | +| Recognizer-Text | The sample uses the Microsoft/Recognizers-Text libraries to parse and validate user input. For more information, see the [overview](https://github.com/Microsoft/Recognizers-Text#microsoft-recognizers-text-overview) page. | ## Next steps -Now that you know how to configure state to help you read and write bot data to storage, let's learn how ask the user a series of questions, validate their answers, and save their input. +Learn how to ask the user a series of questions, validate their answers, and save their input. > [!div class="nextstepaction"] > [Create your own prompts to gather user input](bot-builder-primitive-prompts.md). diff --git a/articles/v4sdk/bot-builder-howto-v4-storage.md b/articles/v4sdk/bot-builder-howto-v4-storage.md index 026ef20a2..4734b9bc5 100644 --- a/articles/v4sdk/bot-builder-howto-v4-storage.md +++ b/articles/v4sdk/bot-builder-howto-v4-storage.md @@ -1,41 +1,52 @@ --- -title: Write directly to storage - Bot Service -description: Learn how to write directly to storage with the Bot Framework SDK for .NET. +title: Write directly to storage +description: Learn how to use the Bot Framework SDK to write bot data directly to various types of persistent storage without using a state manager. keywords: storage, read and write, memory storage, eTag -author: DeniseMak -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 11/01/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: how-to monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Write directly to storage -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -You can read and write directly to your storage object without using middleware or context object. This can be appropriate for data your bot uses to preserve a conversation, or data that comes from a source outside your bot's conversation flow. In this data storage model, data is read in directly from storage instead of using a state manager. The code examples in this article show you how to read and write data to storage using **Memory Storage**, **Cosmos DB**, **Blob Storage**, **Azure Table storage** and **Azure Blob Transcript Store**. +You can read and write directly to your storage object without using middleware or context object. This can be appropriate for data your bot uses to preserve a conversation, or data that comes from a source outside your bot's conversation flow. In this data storage model, data is read in directly from storage instead of using a state manager. The code examples in this article show you how to read and write data to storage using **memory**, **Cosmos DB**, **Azure Blob**, and **Azure Blob transcript** storage. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites + - If you don't have an Azure subscription, create a [free](https://azure.microsoft.com/free/) account before you begin. -- Familiarity with article: Create a bot locally for [dotnet](https://aka.ms/bot-framework-www-c-sharp-quickstart), [nodeJS](https://aka.ms/bot-framework-www-node-js-quickstart) or [Python](https://aka.ms/bot-framework-www-node-python-quickstart). -- Bot Framework SDK v4 template for [C# template](https://aka.ms/bot-vsix), [nodeJS](https://nodejs.org) and [yeoman](http://yeoman.io). +- Familiarity with [Creating a bot](../bot-service-quickstart-create-bot.md) locally. +- Bot Framework SDK v4 templates for [Visual Studio (C#)](https://marketplace.visualstudio.com/items?itemName=BotBuilder.botbuilderv4), [Node.js](https://nodejs.org), or [Yeoman](http://yeoman.io). + +[!INCLUDE [VSIX templates](../includes/vsix-templates-versions.md)] ## About this sample -The sample code in this article begins with the structure of a basic echo bot, then extends that bot's functionality by adding additional code (provided below). This extended code creates a list to preserve user inputs as they are received. Each turn, the full list of user inputs is echoed back to the user. The data structure containing this list of inputs is then saved to storage at the end of that turn. Various types of storage are explored as additional funtionality is added to this sample code. + +The sample code in this article begins with the structure of a basic echo bot, then extends that bot's functionality by adding additional code (provided below). This extended code creates a list to preserve user inputs as they're received. Each turn, the full list of user inputs, saved to memory, is echoed back to the user. The data structure containing this list of inputs is then modified to save to storage. Various types of storage are explored as additional functionality is added to this sample code. + +[!INCLUDE [ropc-connection-strings](../includes/ropc-connection-strings-important.md)] ## Memory storage -The Bot Framework SDK allows you to store user inputs using in-memory storage. Memory storage is used for testing purposes only and is not intended for production use. In-memory storage is volatile and temporary since the data is cleared each time the bot is restarted. Persistent storage types, such as database storage, are best for production bots. Be sure to set storage to **Cosmos DB**, **Blob Storage**, or [**Azure Table storage**](~/nodejs/bot-builder-nodejs-state-azure-table-storage.md) before publishing your bot. +The Bot Framework SDK allows you to store user inputs using in-memory storage. Since in-memory storage is cleared each time the bot is restarted, it's best suited for testing purposes and isn't intended for production use. Persistent storage types, such as database storage, are best for production bots. -#### Build a basic bot +## Build a basic bot -The rest of this topic builds off of an Echo bot. The Echo bot sample code can be locally built by following the Quickstart instructions for building either a [C# EchoBot](https://aka.ms/bot-framework-www-c-sharp-quickstart), [JS EchoBot](https://aka.ms/bot-framework-www-node-js-quickstart) or [Python EchoBot](https://aka.ms/bot-framework-www-node-python-quickstart). +The rest of this topic builds off of an Echo bot. The Echo bot sample code can be locally built by following the quickstart instructions to [Create a bot](../bot-service-quickstart-create-bot.md). ### [C#](#tab/csharp) -**EchoBot.cs** +Replace the code in **EchoBot.cs** with the following code: + ```csharp using System; using System.Threading.Tasks; @@ -60,22 +71,23 @@ public class EchoBot : ActivityHandler // A list of things that users have said to the bot public List UtteranceList { get; } = new List(); - // The number of conversational turns that have occurred + // The number of conversational turns that have occurred public int TurnNumber { get; set; } = 0; // Create concurrency control where this is used. public string ETag { get; set; } = "*"; } - + // Echo back user input. protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { // preserve user input. - var utterance = turnContext.Activity.Text; - // make empty local logitems list. + var utterance = turnContext.Activity.Text; + + // Make empty local log-items list. UtteranceLog logItems = null; - - // see if there are previous messages saved in storage. + + // See if there are previous messages saved in storage. try { string[] utteranceList = { "UtteranceLog" }; @@ -83,23 +95,24 @@ public class EchoBot : ActivityHandler } catch { - // Inform the user an error occured. + // Inform the user an error occurred. await turnContext.SendActivityAsync("Sorry, something went wrong reading your stored messages!"); } - + // If no stored messages were found, create and store a new entry. if (logItems is null) { - // add the current utterance to a new object. + // Add the current utterance to a new object. logItems = new UtteranceLog(); logItems.UtteranceList.Add(utterance); - // set initial turn counter to 1. + + // Set initial turn counter to 1. logItems.TurnNumber++; // Show user new user message. await turnContext.SendActivityAsync($"{logItems.TurnNumber}: The list is now: {string.Join(", ", logItems.UtteranceList)}"); - // Create Dictionary object to hold received user messages. + // Create dictionary object to hold received user messages. var changes = new Dictionary(); { changes.Add("UtteranceLog", logItems); @@ -111,27 +124,27 @@ public class EchoBot : ActivityHandler } catch { - // Inform the user an error occured. + // Inform the user an error occurred. await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!"); } } - // Else, our Storage already contained saved user messages, add new one to the list. + // Else, our storage already contained saved user messages, add new one to the list. else { // add new message to list of messages to display. logItems.UtteranceList.Add(utterance); // increment turn counter. logItems.TurnNumber++; - + // show user new list of saved messages. await turnContext.SendActivityAsync($"{logItems.TurnNumber}: The list is now: {string.Join(", ", logItems.UtteranceList)}"); - + // Create Dictionary object to hold new list of messages. var changes = new Dictionary(); { changes.Add("UtteranceLog", logItems); }; - + try { // Save new list to your Storage. @@ -139,11 +152,10 @@ public class EchoBot : ActivityHandler } catch { - // Inform the user an error occured. + // Inform the user an error occurred. await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!"); } } - ... // OnMessageActivityAsync( ) } } @@ -151,32 +163,48 @@ public class EchoBot : ActivityHandler ### [JavaScript](#tab/javascript) -To use the .env configuration file, your bot needs an extra package included. If not already installed, get the dotnet package from npm: +To use the **.env** configuration file, your bot needs an extra package included. If not already installed, get the .NET package from npm: -```powershell +```console npm install --save dotenv ``` -**bot.js** +Modify the code in **index.js** that creates the main dialog. The existing line of code to create the main dialog is `const myBot = new EchoBot();`, it needs updated to enable passing a storage object to the `EchoBot` constructor so you can store the user's input to the bot's internal memory: + +First you'll need to add a reference to `MemoryStorage` in `botbuilder`: + +```javascript +const { MemoryStorage } = require('botbuilder'); +``` + +Then create the memory storage object, and pass it to the `EchoBot` constructor: + +```javascript +const myStorage = new MemoryStorage(); +const myBot = new EchoBot(myStorage); +``` + +This passes in a `MemoryStorage` object to the `EchoBot` constructor. You'll change that later to pass a _Cosmos DB_ or _Blob Storage_ object. + +Next, replace the code in **bot.js** with the following code: + ```javascript const { ActivityHandler, MemoryStorage } = require('botbuilder'); const restify = require('restify'); -// Add memory storage. -var storage = new MemoryStorage(); - // Process incoming requests - adds storage for messages. -class MyBot extends ActivityHandler { - constructor() { +class EchoBot extends ActivityHandler { + constructor(myStorage) { super(); - // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. - this.onMessage(async turnContext => { console.log('this gets called (message)'); - await turnContext.sendActivity(`You said '${ turnContext.activity.text }'`); + this.storage = myStorage; + // See https://learn.microsoft.com/azure/bot-service/bot-builder-basics to learn more about the message and other activity types. + this.onMessage(async turnContext => { console.log('this gets called (message)'); + await turnContext.sendActivity(`You said '${ turnContext.activity.text }'`); // Save updated utterance inputs. - await logMessageText(storage, turnContext); + await logMessageText(this.storage, turnContext); }); - this.onConversationUpdate(async turnContext => { console.log('this gets called (conversation update)'); - await turnContext.sendActivity('[conversationUpdate event detected]'); }); + this.onConversationUpdate(async turnContext => { console.log('this gets called (conversation update)'); + await turnContext.sendActivity('Welcome, enter an item to save to your list.'); }); } } @@ -225,14 +253,15 @@ async function logMessageText(storage, turnContext) { } } -module.exports.MyBot = MyBot; +module.exports.EchoBot = EchoBot; ``` ### [Python](#tab/python) -**bot.py** -```py +Replace the code in **echo_bot.py** with the following code: + +```python from botbuilder.core import ActivityHandler, TurnContext, StoreItem, MemoryStorage @@ -248,7 +277,7 @@ class UtteranceLog(StoreItem): self.e_tag = "*" -class MyBot(ActivityHandler): +class EchoBot(ActivityHandler): """ Represents a bot saves and echoes back user input. """ @@ -289,464 +318,640 @@ class MyBot(ActivityHandler): --- ### Start your bot + Run your bot locally. -### Start the emulator and connect your bot -- Install the Bot Framework [Emulator](https://aka.ms/bot-framework-emulator-readme) -Next, start the emulator and then connect to your bot in the emulator: +### Start the Emulator and connect your bot -1. Click the **Create new bot configuration** link in the emulator "Welcome" tab. -2. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. +Install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) +Next, start the Emulator and then connect to your bot in the Emulator: + +1. Select the **Create new bot configuration** link in the Emulator **Welcome** tab. +1. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. ### Interact with your bot + Send a message to your bot. The bot will list the messages it has received. -![Emulator test storage bot](./media/emulator-direct-storage-test.png) +:::image type="content" source="./media/emulator-direct-storage-test.png" alt-text="A conversation with the bot that shows the bot keeping a list of messages from the user."::: -## Using Cosmos DB -Now that you've used memory storage, we'll update the code to use Azure Cosmos DB. Cosmos DB is Microsoft's globally distributed, multi-model database. Azure Cosmos DB enables you to elastically and independently scale throughput and storage across any number of Azure's geographic regions. It offers throughput, latency, availability, and consistency guarantees with comprehensive service level agreements (SLAs). +The remainder of this article will demonstrate how to save to persistent storage instead of the bot's internal memory. -### Set up -To use Cosmos DB in your bot, you'll need to create a database resource before getting into the code. For an in-depth description of Cosmos DB database and app creation access the documentation here for [Cosmos DB dotnet](https://aka.ms/Bot-framework-create-dotnet-cosmosdb) or [Cosmos DB nodejs](https://aka.ms/Bot-framework-create-nodejs-cosmosdb). +## Using Cosmos DB -### Create your database account +>[!IMPORTANT] +> The _Cosmos DB storage_ class has been deprecated. Containers originally created with CosmosDbStorage had no partition key set, and were given the default partition key of _\/_partitionKey_. +> +> Containers created with _Cosmos DB storage_ can be used with _Cosmos DB partitioned storage_. Read [Partitioning in Azure Cosmos DB](/azure/cosmos-db/partitioning-overview) for more information. +> +> Also note that, unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage doesn't automatically create a database within your Cosmos DB account. You need to [create a new database manually](/azure/cosmos-db/create-cosmosdb-resources-portal), but skip manually creating a container since _CosmosDbPartitionedStorage_ will create the container for you. -1. In a new browser window, sign in to the [Azure portal](https://portal.azure.com). +Now that you've used memory storage, we'll update the code to use Azure Cosmos DB. Cosmos DB is Microsoft's globally distributed, multi-model database. Azure Cosmos DB enables you to elastically and independently scale throughput and storage across any number of Azure's geographic regions. It offers throughput, latency, availability, and consistency guarantees with comprehensive service level agreements (SLAs). -![Create Cosmos DB database account](./media/create-cosmosdb-database.png) +### Set up a Cosmos DB resource -2. Click **Create a resource > Databases > Azure Cosmos DB** +To use Cosmos DB in your bot, you'll need to create a database resource before getting into the code. For an in-depth description of Cosmos DB database and app creation, see the quickstart for [.NET](/azure/cosmos-db/create-sql-api-dotnet-v4), [Node.js](/azure/cosmos-db/create-sql-api-nodejs), or [Python](/azure/cosmos-db/create-sql-api-python). -![Cosmos DB new account page](./media/cosmosdb-new-account-page.png) +### Create your database account -3. On the **New account page**, provide **Subscription**, **Resource group** information. Create a unique name for your **Account Name** field - this eventually becomes part of your data access URL name. For **API**, select **Core(SQL)**, and provide a nearby **Location** to improve data access times. -4. Then click **Review + Create**. -5. Once validation has been successful, click **Create**. +1. Go to the [Azure portal](https://portal.azure.com) to create an Azure Cosmos DB account. Search for and select **Azure Cosmos DB**. +1. In the **Azure Cosmos DB** page, select **New** to bring up the **Create Azure Cosmos DB Account** page. -The account creation takes a few minutes. Wait for the portal to display the Congratulations! Your Azure Cosmos DB account was created page. + :::image type="content" source="./media/create-cosmosdb-database.png" alt-text="Screenshot of creating your Cosmos DB account."::: -### Add a database +1. Provide values for the following fields: + 1. **Subscription**. Select the Azure subscription that you want to use for this Azure Cosmos account. + 1. **Resource group**. Select an existing resource group or select **Create new**, and enter a name for a new resource group. + 1. **Account name**. Enter a name to identify your Azure Cosmos account. Because _documents.azure.com_ is appended to the name that you provide to create your URI, use a unique name. Note the following guidelines: + - The name must be unique across Azure. + - The name must be between 3 and 31 characters long. + - The name can include only lowercase letters, numbers, and the hyphen (-) character. + 1. **API**. Select **Core(SQL)** + 1. **Location**. select a location that is closest to your users to give them the fastest access to the data. +1. Select **Review + Create**. +1. Once validated, select **Create**. -1. Navigate to the **Data Explorer** page within your newly created Cosmos DB account, then choose **Create Database** from the drop-down box next to the **Create Container** button. A panel will then open on the right hand side of the window, where you can enter the details for the new container. +The account creation takes a few minutes. Wait for the portal to display the _Congratulations! Your Azure Cosmos DB account was created_ page. -![Cosmos DB](./media/create-cosmosdb-database-resource.png) +### Add a database -2. Enter an ID for your new database and, optionally, set the throughput (you can change this later) and finally click **OK** to create your database. Make a note of this database ID for use later on when configuring your bot. +> [!NOTE] +> Don't create the container yourself. Your bot will create it for you when creating its internal Cosmos DB client, ensuring it's configured correctly for storing bot state. -![Cosmos DB](./media/create-cosmosdb-database-resource-details.png) +1. Navigate to the **Data Explorer** page within your newly created Cosmos DB account, then choose **New Database** from the **New Container** drop-down. A panel will then open on the right-hand side of the window, where you can enter the details for the new database. -3. Now that you have created a Cosmos DB account and a database, you need to copy over some of the values for integrating your new database into your bot. To retrieve these, navigate to the **Keys** tab within the database settings section of your Cosmos DB account. From this page you will need your Cosmos DB endpoint (**URI**) and your authorization key (**PRIMARY KEY**). + :::image type="content" source="./media/create-cosmosdb-database-resource.png" alt-text="Screenshot of creating your Cosmos DB database."::: -![Cosmos DB Keys](./media/comos-db-keys.png) +1. Enter an ID for your new database and, optionally, set the throughput (you can change this later) and finally select **OK** to create your database. Make a note of this database ID for use later on when configuring your bot. +1. Now that you've created a Cosmos DB account and a database, you need to copy over some of the values for integrating your new database into your bot. To retrieve these, navigate to the **Keys** tab within the database settings section of your Cosmos DB account. From this page, you'll need your **URI** (_Cosmos DB endpoint_) and your **PRIMARY KEY** (_authorization key_). -You should now have a Cosmos DB account, containing a database and have the following details ready to configure your bot. +You should now have a Cosmos DB account with a database and the following values ready to use in your bot settings. -- Cosmos DB Endpoint -- Authorization Key +- URI +- Primary Key - Database ID -### Add configuration information -Our configuration data to add Cosmos DB storage is short and simple. Use the details you made a note of in the previous part of this article to set your endpoint, authorization key and database ID. Finally, you should choose an appropriate name for the container that will be created within your database to store your bot state. In the example below the container will be called "bot-storage". +### Add Cosmos DB configuration information -> [!NOTE] -> You should not create the container yourself. Your bot will create it for you when creating its internal Cosmos DB client, ensuring it is configured correctly for storing bot state. +Use the details you made a note of in the previous part of this article to set your endpoint, authorization key, and database ID. Finally, you should choose an appropriate name for the container that will be created within your database to store your bot state. In the example below the Cosmos DB container that is created will be named "bot-storage". ### [C#](#tab/csharp) -**EchoBot.cs** -```csharp -public class EchoBot : ActivityHandler -{ - private const string CosmosServiceEndpoint = ""; - private const string CosmosDBKey = ""; - private const string CosmosDBDatabaseId = ""; - private const string CosmosDBContainerId = "bot-storage"; - ... - -} +Add the following information to your configuration file. + +**appsettings.json** + +```json +"CosmosDbEndpoint": "", +"CosmosDbAuthKey": "", +"CosmosDbDatabaseId": "", +"CosmosDbContainerId": "bot-storage" ``` ### [JavaScript](#tab/javascript) -Add the following information to your `.env` file. +Add the following information to your **.env** file. **.env** -```javascript -DB_SERVICE_ENDPOINT="" -AUTH_KEY="" -DATABASE_ID="" -CONTAINER="bot-storage" + +```ini +CosmosDbEndpoint="" +CosmosDbAuthKey="" +CosmosDbDatabaseId="" +CosmosDbContainerId="bot-storage" ``` ### [Python](#tab/python) -Add the following information to your `bot.py` file. +Add the following information to your configuration file. -```javascript -COSMOSDB_SERVICE_ENDPOINT = "" -COSMOSDB_KEY = "" -COSMOSDB_DATABASE_ID = "" -COSMOSDB_CONTAINER_ID = "bot-storage" +**config.py** + +```python +COSMOS_DB_URI="" +COSMOS_DB_PRIMARY_KEY="your-primary-key" +COSMOS_DB_DATABASE_ID="" +COSMOS_DB_CONTAINER_ID="bot-storage" ``` --- -#### Installing packages -Make sure you have the packages necessary for Cosmos DB +#### Installing Cosmos DB packages + +Make sure you've the packages necessary for Cosmos DB. ### [C#](#tab/csharp) -```powershell -Install-Package Microsoft.Bot.Builder.Azure -``` +Install the **Microsoft.Bot.Builder.Azure** NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio using the NuGet Package Manager +](/nuget/consume-packages/install-use-packages-visual-studio). ### [JavaScript](#tab/javascript) -you can add a references to botbuilder-azure in your project via npm. ->**Note** - this npm package relies on an installation of Python existing on your development machine. If you have not previously installed Python you can find installation resources for your machine here: [Python.org](https://www.python.org/downloads/) +Add a reference to **botbuilder-azure** using npm. -```powershell -npm install --save botbuilder-azure +```Console +npm install --save botbuilder-azure ``` -If not already installed, get the dotnet package from npm in order to access your `.env` file settings. +If not already installed, get the .NET package from npm in order to access your **.env** file settings. -```powershell +```Console npm install --save dotenv ``` ### [Python](#tab/python) -you can add a references to botbuilder-azure in your project via pip. +You can add a reference to botbuilder-azure in your project via pip. -```powershell -pip install botbuilder-azure +```Console +pip install botbuilder-azure ``` --- -### Implementation +### Cosmos DB implementation > [!NOTE] -> Version 4.6 introduced a new Cosmos DB storage provider, `CosmosDbPartitionedStorage`. Existing bots using the original `CosmosDbStorage` should continue using `CosmosDbStorage`. Bots using the older provider will continue to work as expected. New bots should use `CosmosDbPartitionedStorage` as partitioning offers increased performance. +> Version 4.6 introduced a new Cosmos DB storage provider, the _Cosmos DB partitioned storage_ class, and the original _Cosmos DB storage_ class is deprecated. Containers created with _Cosmos DB storage_ can be used with _Cosmos DB partitioned storage_. Read [Partitioning in Azure Cosmos DB](/azure/cosmos-db/partitioning-overview) for more information. +> +> Unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage doesn't automatically create a database within your Cosmos DB account. You need to [create a new database manually](/azure/cosmos-db/create-cosmosdb-resources-portal), but skip manually creating a container since _CosmosDbPartitionedStorage_ will create the container for you. ### [C#](#tab/csharp) -The following sample code runs using the same bot code as the [memory storage](#memory-storage) sample provided above. -The code snippet below shows an implementation of Cosmos DB storage for '_myStorage_' that replaces local Memory storage. Memory Storage is commented out and replaced with a reference to Cosmos DB. +The following sample code runs using the same bot code as the [memory storage](#memory-storage) sample provided above, with the exceptions listed here. +The code snippets below show an implementation of Cosmos DB storage for '_myStorage_' that replaces local Memory storage. -**EchoBot.cs** -```csharp +You first need to update **Startup.cs** to reference the _bot builder Azure_ library: -using System; -... +```csharp using Microsoft.Bot.Builder.Azure; -... -public class EchoBot : ActivityHandler -{ - // Create local Memory Storage - commented out. - // private static readonly MemoryStorage _myStorage = new MemoryStorage(); +``` - // Replaces Memory Storage with reference to Cosmos DB. - private static readonly CosmosDbStorage _myStorage = new CosmosDbPartitionedStorage(new CosmosDbPartitionedStorageOptions - { - CosmosDbEndpoint = CosmosServiceEndpoint, - AuthKey = CosmosDBKey, - DatabaseId = CosmosDBDatabaseId, - ContainerId = CosmosDBContainerId, - }); - - ... -} +Next, in the `ConfigureServices` method in **Startup.cs**, create the `CosmosDbPartitionedStorage` object. This will be passed into the `EchoBot` constructor through dependency injection. +```csharp +// Use partitioned CosmosDB for storage, instead of in-memory storage. +services.AddSingleton( + new CosmosDbPartitionedStorage( + new CosmosDbPartitionedStorageOptions + { + CosmosDbEndpoint = Configuration.GetValue("CosmosDbEndpoint"), + AuthKey = Configuration.GetValue("CosmosDbAuthKey"), + DatabaseId = Configuration.GetValue("CosmosDbDatabaseId"), + ContainerId = Configuration.GetValue("CosmosDbContainerId"), + CompatibilityMode = false, + })); ``` -### [JavaScript](#tab/javascript) +In **EchoBot.cs** change the `_myStorage` variable declaration `private static readonly MemoryStorage _myStorage = new MemoryStorage();` to the following: -The following sample code is similar to [memory storage](#memory-storage) but with some slight changes. +```csharp +// variable used to save user input to CosmosDb Storage. +private readonly IStorage _myStorage; +``` -Require `CosmosDbPartitionedStorage` from `botbuilder-azure` and configure dotenv to read the `.env` file. +Then pass in the `IStorage` object to the `EchoBot` constructor: -**bot.js** - -```javascript -const { CosmosDbPartitionedStorage } = require("botbuilder-azure"); +```csharp +public EchoBot(IStorage storage) +{ + if (storage is null) throw new ArgumentNullException(); + _myStorage = storage; +} ``` -Comment out Memory Storage, replace with reference to Cosmos DB. -**bot.js** +### [JavaScript](#tab/javascript) + +First, add code to your **index.js** file to enable you to access the values from your **.env** file that you entered previously: + ```javascript // initialized to access values in .env file. const ENV_FILE = path.join(__dirname, '.env'); require('dotenv').config({ path: ENV_FILE }); +``` + +Next, you'll need to make changes to **index.js** to use Cosmos DB partitioned storage instead of the Bot Frameworks internal storage. All the code changes in this section are made in **index.js**. -// Create local Memory Storage - commented out. -// var storage = new MemoryStorage(); +First, add a reference to **botbuilder-azure** in **index.js**. This will give you access to the `CosmosDbPartitionedStorage` class. -// Create access to CosmosDb Storage - this replaces local Memory Storage. -var storage = new CosmosDbPartitionedStorage({ - cosmosDbEndpoint: process.env.DB_SERVICE_ENDPOINT, - authKey: process.env.AUTH_KEY, - databaseId: process.env.DATABASE_ID, - containerId: process.env.CONTAINER -}) +```javascript +const { CosmosDbPartitionedStorage } = require('botbuilder-azure'); +``` +Next, create a new `CosmosDbPartitionedStorage` object. + +```javascript +const myStorage = new CosmosDbPartitionedStorage({ + cosmosDbEndpoint: process.env.CosmosDbEndpoint, + authKey: process.env.CosmosDbAuthKey, + databaseId: process.env.CosmosDbDatabaseId, + containerId: process.env.CosmosDbContainerId, + compatibilityMode: false +}); +``` + +You can now comment out or remove the `myStorage` const declaration that you previously added, since you no longer are saving user input to the Bot Frameworks internal storage, but instead to Cosmos DB: + +```javascript +//const myStorage = new MemoryStorage(); +const myBot = new EchoBot(myStorage); ``` ### [Python](#tab/python) -The following sample code is similar to [memory storage](#memory-storage) but with some slight changes. +The following sample code runs using the same bot code as the [memory storage](#memory-storage) sample provided above, with the exceptions listed here. -Require `CosmosDbStorage` from `botbuilder-azure` and create the CosmosDBStorage object. +Both `CosmosDbPartitionedStorage` and `CosmosDbPartitionedConfig` from `botbuilder-azure` are required to create the CosmosDBStorage object. -**bot.py** +**echo_bot.py** -```py -from botbuilder.azure import CosmosDbStorage, CosmosDbConfig +```python +from botbuilder.azure import CosmosDbPartitionedStorage, CosmosDbPartitionedConfig ``` -Comment out Memory Storage in `__init__` and replace with reference to Cosmos DB. Use the endpoint, auth key, database id and container id used above. +In order to access the settings in **config.py**, you'll import `DefaultConfig` as shown below: + +```python +from config import DefaultConfig +CONFIG = DefaultConfig() +``` -**bot.py** -```py +Comment out Memory Storage in `__init__` and replace with reference to Cosmos DB. Use the URI (endpoint), Primary key (authorization key), database ID, and container ID used above. + +Next, remove or comment out the Memory Storage code in `__init__` and add a reference to your Cosmos DB information from **config.py**. + +The code snippet below show an implementation of Cosmos DB for 'Storage' that replaces local Memory storage. + +**echo_bot.py** + +```python def __init__(self): - cosmos_config = CosmosDbConfig( - endpoint=COSMOSDB_SERVICE_ENDPOINT, - masterkey=COSMOSDB_KEY, - database=COSMOSDB_DATABASE_ID, - container=COSMOSDB_CONTAINER_ID + cosmos_config = CosmosDbPartitionedConfig( + cosmos_db_endpoint=CONFIG.COSMOS_DB_URI, + auth_key=CONFIG.COSMOS_DB_PRIMARY_KEY, + database_id=CONFIG.COSMOS_DB_DATABASE_ID, + container_id=CONFIG.COSMOS_DB_CONTAINER_ID, + compatibility_mode = False ) - self.storage = CosmosDbStorage(cosmos_config) + self.storage = CosmosDbPartitionedStorage(cosmos_config) ``` --- -## Start your bot +## Start your Cosmos DB bot + Run your bot locally. -## Test your bot with bot framework emulator -Now start your bot framework emulator and connect to your bot: +## Test your Cosmos DB bot with Bot Framework Emulator + +Now start the Bot Framework Emulator and connect to your bot: -1. Click the **Create new bot configuration** link in the emulator "Welcome" tab. -2. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. +1. Select the **create a new bot configuration** link in the Emulator **Welcome** tab. +1. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. + +## Interact with your Cosmos DB bot -## Interact with your bot Send a message to your bot, and the bot will list the messages it received. -![Emulator running](./media/emulator-direct-storage-test.png) +:::image type="content" source="./media/emulator-direct-storage-test.png" alt-text="A conversation with the bot that shows the bot keeping a list of messages from the user."::: -### View your data -After you have run your bot and saved your information, we can view the data stored in the Azure portal under the **Data Explorer** tab. +### View your Cosmos DB data -![Data Explorer example](./media/data_explorer.PNG) +After you've run your bot and saved your information, you can view the data stored in the Azure portal under the **Data Explorer** tab. +:::image type="content" source="./media/data_explorer.PNG" alt-text="Screenshot of the Data Explorer in the Azure portal."::: -## Using Blob storage -Azure Blob storage is Microsoft's object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data, such as text or binary data. +## Using Blob storage -### Create your Blob storage account -To use Blob storage in your bot, you'll need to get a few things set up before getting into the code. -1. In a new browser window, sign in to the [Azure portal](https://portal.azure.com). +Azure Blob storage is Microsoft's object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data, such as text or binary data. This section explains how to create an Azure blob storage account and container, then how to reference your blob storage container from your bot. -![Create Blob storage](./media/create-blob-storage.png) +For more information on Blob Storage, see [What is Azure Blob storage?](/azure/storage/blobs/storage-blobs-overview) -2. Click **Create a resource > Storage > Storage account - blob, file, table, queue** +### Create your Blob storage account -![Blob storage new account page](./media/blob-storage-new-account.png) +To use Blob storage in your bot, you'll need to get a few things set up before getting into the code. -3. In the **New account page**, enter **Name** for the storage account, select **Blob storage** for **Account kind**, provide **Location**, **Resource group** and **Subscription** information. -4. Then click **Review + Create**. -5. Once validation has been successful, click **Create**. +1. In the [Azure portal](https://portal.azure.com), select **All services**. +1. In the **Featured** section of the **All services** page, select **Storage accounts**. +1. In the **Storage accounts** page, select **New**. + + :::image type="content" source="./media/blob-storage-new-account.png" alt-text="Screenshot of creating an Azure Storage account."::: + +1. In the **Subscription** field, select the subscription in which to create the storage account. +1. In the **Resource group** field, select an existing resource group or select **Create new**, and enter a name for the new resource group. +1. In the **Storage account name** field, enter a name for the account. Note the following guidelines: + - The name must be unique across Azure. + - The name must be between 3 and 24 characters long. + - The name can include only numbers and lowercase letters. +1. In the **Location** field, select a location for the storage account, or use the default location. +1. For the rest of the settings, configure the following: + - **Performance**: Standard. [Learn more about performance](/azure/storage/common/storage-account-overview#performance-tiers). + - **Account kind**: BlobStorage. [Learn more about storage accounts](/azure/storage/common/storage-account-create?tabs=azure-portal). + - **Replication**: Leave the default setting. [Learn more about redundancy](/azure/storage/common/storage-redundancy). + +1. In the **Project details** section of the **Create storage account** page, select the desired values for **subscription** and **Resource group**. +1. In the **Instance details** section of the **Create storage account** page, enter the **Storage account name** then select values for **Location**, **Account kind**, and **Replication**. +1. Select **Review + create** to review the storage account settings. +1. Once validated, select **Create**. ### Create Blob storage container -Once your Blob storage account is created, open this account by -1. Selecting the resource. -2. Now "Open" using Storage Explorer (preview) -![Create Blob storage container](./media/create-blob-container.png) +Once your Blob storage account is created, open it, then: + +1. Select **Storage Explorer (Preview)**. +1. Then right-click on **BLOB CONTAINERS** +1. Select **Create blob container** from the drop-down list. -3. Right click BLOB CONTAINERS, select _Create blob container_. -4. Add a name. You will use this name for the value "your-blob-storage-container-name" to provide access to your Blob Storage account. + :::image type="content" source="./media/create-blob-container.png" alt-text="Screenshot of creating a blob container."::: -#### Add configuration information -Find the Blob Storage keys you need to configure Blob Storage for your bot as shown above: -1. In the Azure portal, open your Blob Storage account and select **Settings > Access keys**. +1. Enter a name in the **New container** form. You'll use this name for the value of your "_blob container name_" to provide access to your Blob storage account. Note the following guidelines: + - This name may only contain lowercase letters, numbers, and hyphens. + - This name must begin with a letter or a number. + - Each hyphen must be preceded and followed by a valid non-hyphen character. + - The name must be between 3 and 63 characters long. -![Find Blob Storage Keys](./media/find-blob-storage-keys.png) +#### Add Blob storage configuration information -We will use key1 _Connection string_ as the value "your-blob-storage-account-string" to provide access to your Blob Storage account. +Find the Blob storage keys you need to configure Blob storage for your bot as shown above: -#### Installing packages -If not previously installed to use Cosmos DB, install the following packages. +1. In the Azure portal, open your Blob storage account and select **Access keys** in the **Settings** section. +1. To configure your bot to access to your Blob storage account, use **Connection string** as the value for the _blob connection string_. ### [C#](#tab/csharp) -```powershell -Install-Package Microsoft.Bot.Builder.Azure +Add the following information to your configuration file. + +**appsettings.json** + +```json +"BlobConnectionString": "", +"BlobContainerName": "", +``` + +### [JavaScript](#tab/javascript) + +Add the following information to your **.env** file. + +**.env** + +```ini +BlobConnectionString="" +BlobContainerName="" +``` + +### [Python](#tab/python) + +Add the following information to your configuration file. Use the same container name and connection string used when creating your [blob storage container](#create-blob-storage-container). + +**config.py** + +```python +BLOB_CONNECTION_STRING="" +BLOB_CONTAINER_NAME="" ``` +--- + +#### Installing Blob storage packages + +If not previously installed, install the following packages. + +### [C#](#tab/csharp) + +Install the **Microsoft.Bot.Builder.Azure.Blobs** NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio using the NuGet Package Manager](/nuget/consume-packages/install-use-packages-visual-studio). + ### [JavaScript](#tab/javascript) -Add references to botbuilder-azure in your project via npm. ->**Note** - this npm package relies on an installation of Python existing on your development machine. If you have not previously installed Python you can find installation resources for your machine here: [Python.org](https://www.python.org/downloads/) +Add references to `botbuilder-azure-blobs` in your project via npm. + +> [!NOTE] +> This npm package relies on an installation of Python existing on your development machine. If you've not previously installed Python you can find installation resources for your machine at [Python.org](https://www.python.org/downloads/) -```powershell -npm install --save botbuilder-azure +```Console +npm install --save botbuilder-azure-blobs ``` -If not already installed, get the dotnet package from npm in order to access your `.env` file settings. +If not already installed, get the .NET package from npm in order to access your **.env** file settings. -```powershell +```Console npm install --save dotenv ``` ### [Python](#tab/python) -you can add a references to botbuilder-azure in your project via pip. +you can add a reference to botbuilder-azure in your project via pip. -```powershell -pip install botbuilder-azure +```Console +pip install botbuilder-azure ``` --- -### Implementation +### Blob storage implementation + +_Blob storage_ is used to store bot state. ### [C#](#tab/csharp) -**EchoBot.cs** +> [!NOTE] +> As of version 4.10, `Microsoft.Bot.Builder.Azure.AzureBlobStorage` is deprecated. Use the new `Microsoft.Bot.Builder.Azure.Blobs.BlobsStorage` in its place. + +The following sample code runs using the same bot code as the [memory storage](#memory-storage) sample provided above, with the exceptions listed here. + +The code snippets below show an implementation of Blob storage for '_myStorage_' that replaces local Memory storage. + +You first need to update **Startup.cs** to reference the _bot builder Azure blobs_ library: + +**Startup.cs** + ```csharp -using Microsoft.Bot.Builder.Azure; +using Microsoft.Bot.Builder.Azure.Blobs; ``` -Update the line of code that points "_myStorage_" to your existing Blob Storage account. + +Next, in the `ConfigureServices` method in **Startup.cs**, create the `BlobsStorage` object, passing in the values from `appsettings.json`. This will be passed into the `EchoBot` constructor through dependency injection. + +```csharp +//Use Azure Blob storage, instead of in-memory storage. +services.AddSingleton( + new BlobsStorage( + Configuration.GetValue("BlobConnectionString"), + Configuration.GetValue("BlobContainerName") + )); +``` + +Now you first need to update **EchoBot.cs** to reference the _bot builder Azure blobs_ library: **EchoBot.cs** + ```csharp -private static readonly AzureBlobStorage _myStorage = new AzureBlobStorage("", ""); +using Microsoft.Bot.Builder.Azure.Blobs; ``` -### [JavaScript](#tab/javascript) +Next, remove or comment out the line of code that creates the MemoryStorage variable 'private static readonly MemoryStorage _myStorage = new MemoryStorage();', and create a new variable that will be used to save user input to the Blob Storage. -Add the following information to your `.env` file. +**EchoBot.cs** -**.env** -```javascript -BLOB_NAME="" -BLOB_STRING="" +```csharp +// variable used to save user input to CosmosDb Storage. +private readonly IStorage _myStorage; ``` -Update your `bot.js` file as follows. Require `BlobStorage` from `botbuilder-azure` +Then pass in the `IStorage` object to the `EchoBot` constructor: -**bot.js** -```javascript -const { BlobStorage } = require("botbuilder-azure"); +```csharp +public EchoBot(IStorage storage) +{ + if (storage is null) throw new ArgumentNullException(); + _myStorage = storage; +} ``` -If you did not add code to load your `.env` file to implement Cosmos DB storage, add this here. +Once your storage is set to point to your Blob Storage account, your bot code will now store and retrieve data from Blob storage. + +### [JavaScript](#tab/javascript) + +All the code changes in this section are made in **index.js**. + +First, add code to enable you to access the values from your **.env** file that you entered previously: ```javascript // initialized to access values in .env file. const ENV_FILE = path.join(__dirname, '.env'); require('dotenv').config({ path: ENV_FILE }); ``` -Now update your code to point "_storage_" to your existing Blob Storage account, by commenting out previous storage definitions and adding the following. -**bot.js** +Next, you'll need to make changes use Blob storage instead of the Bot Frameworks internal storage. + +Next, add a reference to `botbuilder-azure-blobs`. This will give you access to the Blob Storage API: + ```javascript -var storage = new BlobStorage({ - containerName: process.env.BLOB_NAME, - storageAccountOrConnectionString: process.env.BLOB_STRING -}); +const { BlobsStorage } = require("botbuilder-azure-blobs"); ``` +To modify your code to use the `BlobsStorage` class, modify your `myStorage` declaration as follows: +```javascript +const myStorage = new BlobsStorage( + process.env.BlobConnectionString, + process.env.BlobContainerName +); +``` + +Once your storage is set to point to your Blob Storage account, your bot code will now store and retrieve data from Blob storage. ### [Python](#tab/python) -The following sample code is similar to [memory storage](#memory-storage) but with some slight changes. +The following sample code runs using the same bot code as the [memory storage](#memory-storage) sample provided above, with the exceptions listed here. -Require `BlobStorage` from `botbuilder-azure` and create the CosmosDBStorage object. +This will require `BlobStorage` and `BlobStorageSettings` from `botbuilder-azure`. -**bot.py** +**echo_bot.py** -```py +```python from botbuilder.azure import BlobStorage, BlobStorageSettings ``` -Comment out Memory Storage in `__init__` and replace with reference to Cosmos DB. Use the container name and connection string used above. +In order to access the settings in **config.py**, you'll import `DefaultConfig` as shown below: + +```python +from config import DefaultConfig +CONFIG = DefaultConfig() +``` + +Next, remove or comment out the Memory Storage code in `__init__` and add a reference to your blob storage information from **config.py**. + +The code snippet below shows an implementation of Blob Storage that replaces the local Memory storage. + +**echo_bot.py** -**bot.py** -```py +```python def __init__(self): blob_settings = BlobStorageSettings( - container_name="", - connection_string="" + connection_string=CONFIG.BLOB_CONNECTION_STRING, + container_name=CONFIG.BLOB_CONTAINER_NAME ) self.storage = BlobStorage(blob_settings) ``` --- -Once your storage is set to point to your Blob Storage account, your bot code will now store and retrieve data from Blob Storage. +Once your storage is set to point to your Blob Storage account, your bot code will now store and retrieve data from Blob storage. + +## Start your Blob storage bot -## Start your bot Run your bot locally. -## Start the emulator and connect your bot -Next, start the emulator and then connect to your bot in the emulator: +## Start the Emulator and connect your Blob storage bot + +Next, start the Emulator and then connect to your bot in the Emulator: -1. Click the **Create new bot configuration** link in the emulator "Welcome" tab. -2. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. +1. Select the **Create new bot configuration** link in the Emulator "Welcome" tab. +1. Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot. + +## Interact with your Blob storage bot -## Interact with your bot Send a message to your bot, and the bot will list the messages it receives. -![Emulator test storage bot](./media/emulator-direct-storage-test.png) +:::image type="content" source="./media/emulator-direct-storage-test.png" alt-text="A conversation with the bot that shows the bot keeping a list of messages from the user."::: + +### View your Blob storage data -### View your data -After you have run your bot and saved your information, we can view it in under the **Storage Explorer** tab in the Azure portal. +After you've run your bot and saved your information, we can view it in under the **Storage Explorer** tab in the Azure portal. ## Blob transcript storage -Azure blob transcript storage provides a specialized storage option that allows you to easily save and retrieve user conversations in the form of a recorded transcript. Azure blob transcript storage is particularly useful for automatically capturing user inputs to examine while debugging your bot's performance. -**NOTE: Javascript and Python do not currently support AzureBlobTranscriptStore. The following directions are for C# only** +Azure blob transcript storage provides a specialized storage option that allows you to easily save and retrieve user conversations in the form of a recorded transcript. Azure blob transcript storage is useful for automatically capturing user inputs to examine while debugging your bot's performance. -### Set up -Azure blob transcript storage can use the same blob storage account created following the steps detailed in sections "_Create your blob storage account_" and "_Add configuration information_" above. We now add a container to hold our transcripts +> [!NOTE] +> Python doesn't currently support _Azure Blob transcript storage_. +> While JavaScript supports Blob transcript storage, the following directions are for C# only. +### Set up a Blob transcript storage container -![Create transcript container](./media/create-blob-transcript-container.png) +Azure blob transcript storage can use the same blob storage account created following the steps detailed in sections "_Create your blob storage account_" and "_Add configuration information_" above. We now add a container to hold our transcripts + +:::image type="content" source="./media/create-blob-transcript-container.png" alt-text="Screenshot of creating a blob container to use as a transcript store."::: 1. Open your Azure blob storage account. -1. Click on _Storage Explorer_. -1. Right click on _BLOB CONTAINERS_ and select _create blob container_. -1. enter a name for your transcript container and then select _OK_. (We entered mybottranscripts) +1. Select **Storage Explorer**. +1. Right click on _BLOB CONTAINERS_ and select **create blob container**. +1. Enter a name for your transcript container and then select **OK**. (We entered _mybottranscripts_) + +### Blob transcript storage implementation + +The following code connects transcript storage pointer `_myTranscripts` to your new Azure blob transcript storage account. To create this link with a new container name, \, it creates a new container within Blob storage to hold your transcript files. -### Implementation -The following code connects transcript storage pointer `_myTranscripts` to your new Azure blob transcript storage account. To create this link with a new container name, , creates a new container within Blob storage to hold your transcript files. +_Blob transcript storage_ is designed to store bot transcripts. + +> [!NOTE] +> As of version 4.10, `Microsoft.Bot.Builder.Azure.AzureBlobTranscriptStore` is deprecated. Use the new `Microsoft.Bot.Builder.Azure.Blobs.BlobsTranscriptStore` in its place. **echoBot.cs** + ```csharp -using Microsoft.Bot.Builder.Azure; +using Microsoft.Bot.Builder.Azure.Blobs; public class EchoBot : ActivityHandler { ... - - private readonly AzureBlobTranscriptStore _myTranscripts = new AzureBlobTranscriptStore("", ""); - + + private readonly BlobsTranscriptStore _myTranscripts = new BlobsTranscriptStore("", ""); + ... } ``` -### Store user conversations in azure blob transcripts -After a blob container is available to store transcripts you can begin to preserve your users' conversations with your bot. These conversations can later be used as a debugging tool to see how users interact with your bot. Each emulator _Restart conversation_ initiates the creation of a new transcript conversation list. The following code preserves user conversation inputs within a stored transcript file. +### Store user conversations in Azure blob transcripts + +After a blob container is available to store transcripts, you can begin to preserve your users' conversations with your bot. These conversations can later be used as a debugging tool to see how users interact with your bot. Each Emulator _Restart conversation_ initiates the creation of a new transcript conversation list. The following code preserves user conversation inputs within a stored transcript file. + - The current transcript is saved using `LogActivityAsync`. - Saved transcripts are retrieved using `ListTranscriptsAsync`. -In this sample code the Id of each stored transcript is saved into a list named "storedTranscripts". This list is later used to manage the number of stored blob transcripts we retain. +In this sample code, the ID of each stored transcript is saved into a list named "storedTranscripts". This list is later used to manage the number of stored blob transcripts we retain. **echoBot.cs** + ```csharp protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) @@ -768,16 +973,18 @@ protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) @@ -799,7 +1006,7 @@ protected override async Task OnMessageActivityAsync(ITurnContext +In our bot code example, we set the `eTag` property of each `IStoreItem` to `*`. The `eTag` (entity tag) member of your store object is used within Cosmos DB to manage concurrency. The `eTag` tells your database what to do if another instance of the bot has changed the object in the same storage that your bot is writing to. #### Last write wins - allow overwrites -An `eTag` property value of asterisk (`*`) indicates that the last writer wins. When creating a new data store, you can set `eTag` of a property to `*` to indicate that you have not previously saved the data that you are writing, or that you want the last writer to overwrite any previously saved property. If concurrency is not an issue for your bot, setting the `eTag` property to `*` for any data that you are writing enables overwrites. + +An `eTag` property value of asterisk (`*`) indicates that the last writer wins. When creating a new data store, you can set `eTag` of a property to `*` to indicate that you've not previously saved the data that you're writing, or that you want the last writer to overwrite any previously saved property. If concurrency isn't an issue for your bot, setting the `eTag` property to `*` for any data that you're writing enables overwrites. #### Maintain concurrency and prevent overwrites -When storing your data into Cosmos DB, use a value other than `*` for the `eTag` if you want to prevent concurrent access to a property and avoid overwriting changes from another instance of the bot. The bot receives an error response with the message `etag conflict key=` when it attempts to save state data and the `eTag` is not the same value as the `eTag` in storage. -By default, the Cosmos DB store checks the `eTag` property of a storage object for equality every time a bot writes to that item, and then updates it to a new unique value after each write. If the `eTag` property on write doesn't match the `eTag` in storage, it means another bot or thread changed the data. +When storing your data into Cosmos DB, use a value other than `*` for the `eTag` if you want to prevent concurrent access to a property and avoid overwriting changes from another instance of the bot. The bot receives an error response with the message `etag conflict key=` when it attempts to save state data and the `eTag` isn't the same value as the `eTag` in storage. + +By default, the Cosmos DB store checks the `eTag` property of a storage object for equality every time a bot writes to that item, and then updates it to a new unique value after each write. If the `eTag` property on write doesn't match the `eTag` in storage, it means another bot or thread changed the data. For example, let's say you want your bot to edit a saved note, but you don't want your bot to overwrite changes that another instance of the bot has done. If another instance of the bot has made edits, you want the user to edit the version with the latest updates. @@ -847,6 +1055,7 @@ For example, let's say you want your bot to edit a saved note, but you don't wan First, create a class that implements `IStoreItem`. **EchoBot.cs** + ```csharp public class Note : IStoreItem { @@ -859,6 +1068,7 @@ public class Note : IStoreItem Next, create an initial note by creating a storage object, and add the object to your store. **EchoBot.cs** + ```csharp // create a note for the first time, with a non-null, non-* ETag. var note = new Note { Name = "Shopping List", Contents = "eggs", ETag = "x" }; @@ -873,6 +1083,7 @@ await NoteStore.WriteAsync(changes, cancellationToken); Then, access and update the note later, keeping its `eTag` that you read from the store. **EchoBot.cs** + ```csharp var note = NoteStore.ReadAsync("Note").Result?.FirstOrDefault().Value; @@ -889,13 +1100,13 @@ if (note != null) If the note was updated in the store before you write your changes, the call to `Write` will throw an exception. - ### [JavaScript](#tab/javascript) Add a helper function to the end of your bot that will write a sample note to a data store. First create `myNoteData` object. **bot.js** + ```javascript // Helper function for writing a sample note to a data store async function createSampleNote(storage, context) { @@ -904,15 +1115,16 @@ async function createSampleNote(storage, context) { contents: "eggs", // If any Note file is already stored, the eTag field // must be set to "*" in order to allow writing without first reading the stored eTag - // otherwise you'll likely get an exception indicating an eTag conflict. + // otherwise you'll likely get an exception indicating an eTag conflict. eTag: "*" } } ``` -Within the `createSampleNote` helper function initialize a `changes` object and add your *notes* to it, then write it to storage. +Within the `createSampleNote` helper function, initialize a `changes` object and add your *notes* to it, then write it to storage. **bot.js** + ```javascript // Write the note data to the "Note" key var changes = {}; @@ -934,6 +1146,7 @@ try { The helper function is accessed from within your bot's logic by adding the following call: **bot.js** + ```javascript // Save a note with etag. await createSampleNote(storage, turnContext); @@ -942,6 +1155,7 @@ await createSampleNote(storage, turnContext); Once created, to later retrieve and update the note we create another helper function that can be accessed when the user types in "update note". **bot.js** + ```javascript async function updateSampleNote(storage, context) { try { @@ -956,7 +1170,7 @@ async function updateSampleNote(storage, context) { await storage.write(note); // Write the changes back to storage var list = note["Note"].contents; await context.sendActivity(`Successfully updated note: ${list}`); - } catch (err) { + } catch (err) { console.log(`Write failed: ${err}`); } } @@ -969,6 +1183,7 @@ async function updateSampleNote(storage, context) { This helper function is accessed from within your bot's logic by adding the following call: **bot.js** + ```javascript // Update a note with etag. await updateSampleNote(storage, turnContext); @@ -980,8 +1195,9 @@ If the note was updated in the store by another user before you attempted to wri First, create a class that implements `StoreItem`. -**bot.py** -```py +**echo_bot.py** + +```python class Note(StoreItem): def __init__(self, name: str, contents: str, e_tag="*"): super(Note, self).__init__() @@ -992,8 +1208,9 @@ class Note(StoreItem): Next, create an initial note by creating a storage object, and add the object to your store. -**bot.py** -```py +**echo_bot.py** + +```python # create a note for the first time, with a non-null, non-* ETag. changes = {"Note": Note(name="Shopping List", contents="eggs", e_tag="x")} @@ -1002,8 +1219,9 @@ await self.storage.write(changes) Then, access and update the note later, keeping its `eTag` that you read from the store. -**bot.py** -```py +**echo_bot.py** + +```python store_items = await self.storage.read(["Note"]) note = store_items["Note"] note.contents = note.contents + ", bread" @@ -1019,7 +1237,8 @@ If the note was updated in the store before you write your changes, the call to To maintain concurrency, always read a property from storage, then modify the property you read, so that the `eTag` is maintained. If you read user data from the store, the response will contain the eTag property. If you change the data and write updated data to the store, your request should include the eTag property that specifies the same value as you read earlier. However, writing an object with its `eTag` set to `*` will allow the write to overwrite any other changes. ## Next steps -Now that you know how to read read and write directly from storage, lets take a look at how you can use the state manager to do that for you. + +Now that you know how to read and write directly from storage, lets take a look at how you can use the state manager to do that for you. > [!div class="nextstepaction"] > [Save state using conversation and user properties](bot-builder-howto-v4-state.md) diff --git a/articles/v4sdk/bot-builder-primitive-prompts.md b/articles/v4sdk/bot-builder-primitive-prompts.md index b1207dc62..b0fb7b497 100644 --- a/articles/v4sdk/bot-builder-primitive-prompts.md +++ b/articles/v4sdk/bot-builder-primitive-prompts.md @@ -1,28 +1,32 @@ --- -title: Create your own prompts to gather user input - Bot Service +title: Create your own prompts to gather user input description: Learn how to manage a conversation flow with primitive prompts in the Bot Framework SDK. keywords: conversation flow, prompts, conversation state, user state, custom prompts author: JonathanFingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/7/2020 +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Create your own prompts to gather user input -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] A conversation between a bot and a user often involves asking (prompting) the user for information, parsing the user's response, and then acting on that information. Your bot should track the context of a conversation, so that it can manage its behavior and remember answers to previous questions. A bot's *state* is information it tracks to respond appropriately to incoming messages. > [!TIP] -> The dialogs library provides built in prompts that provide more functionality that users can use. Examples of those prompts can be found in the [Implement sequential conversation flow](bot-builder-dialog-manage-conversation-flow.md) article. +> The dialogs library provides built-in prompts that provide more functionality that users can use. Examples of those prompts can be found in the [Implement sequential conversation flow](bot-builder-dialog-manage-conversation-flow.md) article. + +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites -- The code in this article is based on the Prompt Users for Input sample. You'll need a copy of either the **[C# sample](https://aka.ms/cs-primitive-prompt-sample), [JavaScript sample](https://aka.ms/js-primitive-prompt-sample), or [Python sample](https://aka.ms/python-primitive-prompt-sample)**. +- The code in this article is based on the Prompt Users for Input sample. You'll need a copy of the [C# sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/44.prompt-users-for-input), [JavaScript sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/44.prompt-for-user-input), [Java sample](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/44.prompt-users-for-input), or [Python sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/44.prompt-for-user-input). - Knowledge of [managing state](bot-builder-concept-state.md) and how to [save user and conversation data](bot-builder-howto-v4-state.md). ## About the sample code @@ -31,34 +35,42 @@ The sample bot asks the user a series of questions, validates some of their answ ## [C#](#tab/csharp) -![custom-prompts](media/CustomPromptBotSample-Overview.png) +:::image type="content" source="media/CustomPromptBotSample-Overview.png" alt-text="Class diagram for the C# sample."::: - A `UserProfile` class for the user information that the bot will collect. - A `ConversationFlow` class to control our conversation state while gathering user information. -- An inner `ConversationFlow.Question` enumeration for tracking where we are in the conversation. +- An inner `ConversationFlow.Question` enumeration for tracking where you are in the conversation. ## [JavaScript](#tab/javascript) -![custom-prompts](media/CustomPromptBotSample-JS-Overview.png) +:::image type="content" source="media/CustomPromptBotSample-JS-Overview.png" alt-text="Class diagram for the JavaScript sample."::: - A `userProfile` class for the user information that the bot will collect. - A `conversationFlow` class to control our conversation state while gathering user information. -- An inner `conversationFlow.question` enumeration for tracking where we are in the conversation. +- An inner `conversationFlow.question` enumeration for tracking where you are in the conversation. + +## [Java](#tab/java) + +:::image type="content" source="media/CustomPromptBotSample-Overview.png" alt-text="Class diagram for the Java sample."::: + +- A `UserProfile` class for the user information that the bot will collect. +- A `ConversationFlow` class to control our conversation state while gathering user information. +- An inner `ConversationFlow.Question` enumeration for tracking where you are in the conversation. ## [Python](#tab/python) -![custom-prompts](media/CustomPromptBotSample-Python-Overview.png) +:::image type="content" source="media/CustomPromptBotSample-Python-Overview.png" alt-text="Class diagram for the Python sample."::: - A `UserProfile` class for the user information that the bot will collect. - A `ConversationFlow` class to control our conversation state while gathering user information. -- An inner `ConversationFlow.Question` enumeration for tracking where we are in the conversation. +- An inner `ConversationFlow.Question` enumeration for tracking where you are in the conversation. --- -The user state will track the user's name, age, and chosen date, and conversation state will track what we've just asked the user. -Since we don't plan to deploy this bot, we'll configure both user and conversation state to use _memory storage_. +The user state will track the user's name, age, and chosen date, and conversation state will track what you last asked the user. +Since you don't plan to deploy this bot, you'll configure user and conversation state to use _memory storage_. -We use the bot's message turn handler plus user and conversation state properties to manage the flow of the conversation and the collection of input. In our bot, we'll record the state property information received during each iteration of the message turn handler. +You use the bot's message turn handler plus user and conversation state properties to manage the flow of the conversation and the collection of input. In your bot, you'll record the state property information received during each iteration of the message turn handler. ## Create conversation and user objects @@ -66,10 +78,10 @@ We use the bot's message turn handler plus user and conversation state propertie Create the user and conversation state objects at startup and consume them via dependency injection in the bot constructor. -**Startup.cs** -[!code-csharp[Startup.cs](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Startup.cs?range=28-35)] +**Startup.cs** +[!code-csharp[Memory, state, and bot](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Startup.cs?range=27-37)] -**Bots/CustomPromptBot.cs** +**Bots/CustomPromptBot.cs** [!code-csharp[constructor](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Bots/CustomPromptBot.cs?range=21-28)] ## [JavaScript](#tab/javascript) @@ -77,20 +89,31 @@ Create the user and conversation state objects at startup and consume them via d Create the user and conversation state objects in **index.js** and consume them in the bot constructor. **index.js** -[!code-javascript[index.js](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/index.js?range=33-39)] +[!code-javascript[index.js](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/index.js?range=49-52)] **bots/customPromptBot.js** [!code-javascript[constructor](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=20-22)] [!code-javascript[constructor](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=27-29)] +## [Java](#tab/java) +Construct the CustomPromptBot in the getBot method using the ConversationState and UserState instances provided by the Spring container. The constructor of CustomPromptBot will store references to the ConversationState and UserState provided during startup. + +**Application.java** +[!code-java[Application.java](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/Application.java?range=51-57)] + +**CustomPromptBot.java** +[!code-java[constructor](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=37-43)] + ## [Python](#tab/python) Create the user and conversation state objects in **app.py** and consume them in the bot constructor. **app.py** -[!code-python[app.py](~/../botbuilder-samples/samples/python/44.prompt-for-user-input/app.py?range=67-73)] + +[!code-python[app.py](~/../botbuilder-samples/samples/python/44.prompt-for-user-input/app.py?range=68-74)] **bots/custom_prompt_bot.py** + [!code-python[constructor](~/../botbuilder-samples/samples/python/44.prompt-for-user-input/bots/custom_prompt_bot.py?range=29-41)] --- @@ -119,9 +142,20 @@ Before the turn ends, call `saveChanges` to write any state changes to storage. [!code-javascript[custom prompt bot](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=42-51)] +## [Java](#tab/java) + +Create property accessors for the user profile and conversation flow properties and then call `get` to retrieve the property value from state. + +**CustomPromptBot.java** +[!code-java[OnMessageActivityAsync](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=45-56)] + +Before the turn ends, call `saveChanges` to write any state changes to storage. + +[!code-java[OnMessageActivityAsync](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=57-59)] + ## [Python](#tab/python) -In the constructor, we create the state property accessors and set up the state management objects (created above) for our conversation. +In the constructor, you create the state property accessors and set up the state management objects (created above) for our conversation. **bots/custom_prompt_bot.py** [!code-python[on_message_activity](~/../botbuilder-samples/samples/python/44.prompt-for-user-input/bots/custom_prompt_bot.py?range=46-49)] @@ -132,20 +166,25 @@ Before the turn ends, call `SaveChangesAsync` to write any state changes to stor --- -## The bot's message turn handler +## Message turn handler When handling message activities, the message handler uses a helper method to manage the conversation and prompt the user. The helper method is described in the following section. ## [C#](#tab/csharp) -**Bots/CustomPromptBot.cs** +**Bots/CustomPromptBot.cs** [!code-csharp[message handler](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Bots/CustomPromptBot.cs?range=30-44)] ## [JavaScript](#tab/javascript) -**bots/customPromptBot.js** +**bots/customPromptBot.js** [!code-javascript[message handler](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=31-39)] +## [Java](#tab/java) + +**CustomPromptBot.java** +[!code-java[message handler](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=45-59)] + ## [Python](#tab/python) **bots/custom_prompt_bot.py** @@ -167,14 +206,19 @@ The validation methods are described in the following section. ## [C#](#tab/csharp) -**Bots/CustomPromptBot.cs** +**Bots/CustomPromptBot.cs** [!code-csharp[FillOutUserProfileAsync](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Bots/CustomPromptBot.cs?range=46-103)] ## [JavaScript](#tab/javascript) -**bots/customPromptBot.js** +**bots/customPromptBot.js** [!code-javascript[fillOutUserProfile](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=53-118)] +## [Java](#tab/java) + +**CustomPromptBot.java** +[!code-java[FillOutUserProfileAsync](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=61-132)] + ## [Python](#tab/python) **bots/custom_prompt_bot.py** @@ -192,19 +236,23 @@ The bot uses the following criteria to validate input. It's normalized by returning just the date portion of the parsed input. > [!NOTE] -> For the age and date input, we use the [Microsoft/Recognizers-Text](https://github.com/Microsoft/Recognizers-Text/) libraries to perform the initial parsing. -> While we provide sample code, we do not explain how the text recognizers libraries work, and this is just one way to parse the input. -> For more information about these libraries, see the repository's **README**. +> For the age and date input, the sample uses the [Microsoft/Recognizers-Text](https://github.com/Microsoft/Recognizers-Text/) libraries to perform the initial parsing. +> This is just one way to parse the input. For more information about these libraries, see the project's [README](https://github.com/Microsoft/Recognizers-Text#readme). ## [C#](#tab/csharp) -**Bots/CustomPromptBot.cs** +**Bots/CustomPromptBot.cs** [!code-csharp[validation methods](~/../botbuilder-samples/samples/csharp_dotnetcore/44.prompt-users-for-input/Bots/CustomPromptBot.cs?range=105-203)] ## [JavaScript](#tab/javascript) -**bots/customPromptBot.cs** -[!code-javascript[validation methods](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=120-190)] +**bots/customPromptBot.js** +[!code-javascript[validation methods](~/../botbuilder-samples/samples/javascript_nodejs/44.prompt-for-user-input/bots/customPromptBot.js?range=120-191)] + +## [Java](#tab/java) + +**CustomPromptBot.java** +[!code-csharp[validation methods](~/../botbuilder-samples/samples/java_springboot/44.prompt-users-for-input/src/main/java/com/microsoft/bot/sample/promptusersforinput/CustomPromptBot.java?range=134-221)] ## [Python](#tab/python) @@ -215,12 +263,10 @@ The bot uses the following criteria to validate input. ## Test the bot locally -Download and install the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) to test the bot locally. - -1. Run the sample locally on your machine. If you need instructions, refer to the README file for [C# sample](https://aka.ms/cs-primitive-prompt-sample), [JS sample](https://aka.ms/js-primitive-prompt-sample), or the [Python sample](https://aka.ms/python-primitive-prompt-sample). -1. Test it using the emulator as shown below. +Download and install the [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) to test the bot locally. -![primitive-prompts](media/primitive-prompts.png) +1. Run the sample locally on your machine. If you need instructions, refer to the `README` file for [C# sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/44.prompt-users-for-input), [JS sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/44.prompt-for-user-input), or the [Python sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/44.prompt-for-user-input). +1. Test it using the Emulator. ## Additional resources diff --git a/articles/v4sdk/bot-builder-security-enhanced.md b/articles/v4sdk/bot-builder-security-enhanced.md new file mode 100644 index 000000000..3db6cebd8 --- /dev/null +++ b/articles/v4sdk/bot-builder-security-enhanced.md @@ -0,0 +1,124 @@ +--- +title: Direct Line enhanced authentication +description: Learn about potential security risks when users connect to a bot and how Direct Line enhanced authentication can mitigate some risks. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +# Direct Line enhanced authentication + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +This article describes potential security risks when users connect to a bot, for example using the [Web Chat](../bot-service-channel-connect-webchat.md#embed-the-web-chat-control-in-a-web-page) control. Also, it shows mitigating solutions using the Direct Line [enhanced authentication settings](../bot-service-channel-connect-directline.md#configure-settings) and secure _user ID_ handling. + +There are two user identities: + +- The channel user's identity. An attacker can use it for [Impersonation](#impersonation). +- The user's identity from the identity provider that the bot uses to authenticate the user. An attacker can use it for [Identity spoofing](#identity-spoofing). + +## Impersonation + +Impersonation refers to the action of an attacker who makes the bot think that they're someone else. For example, in Web Chat, the attacker can impersonate someone else by _changing the user ID_ of the Web Chat instance. + +### Impersonation mitigation + +- Make the user ID _unguessable_. +- [Connect a bot to Direct Line](../bot-service-channel-connect-directline.md). +- Enable the Direct Line channel's [enhanced authentication](../bot-service-channel-connect-directline.md#configure-settings) option to allow the Azure AI Bot Service to further detect and reject any user ID change. This means the user ID (`Activity.From.Id`) on messages from Direct Line to the bot will always be the same as the one you used to initialize the Web Chat control. + + > [!NOTE] + > Direct Line creates a _token_ based on the Direct Line secret and embeds the `User.Id` in the token. + > It assures that the messages sent to the bot have that `User.Id` as the activity's `From.Id`. If a client sends a message to Direct Line having a different `From.Id`, it will be changed to the _ID embedded in the token_ before forwarding the message to the bot. So you can't use another user ID after a channel secret is initialized with that ID. + + This feature requires the user ID to start with `dl_` as shown below. + + > [!TIP] + > For a regional bot, set `dlUrl` according to the selected region.
+ If selected europe, set + "https://europe.directline.botframework.com/v3/directline/tokens/generate".
+ If selected india, set "https://india.directline.botframework.com/v3/directline/tokens/generate".
+ > For more information about regional bots, see [Regionalization in Azure AI Bot Service](bot-builder-concept-regionalization.md). + + ```csharp + public class HomeController : Controller + { + private const string secret = ""; + private const string dlUrl = "https://directline.botframework.com/v3/directline/tokens/generate"; + + public async Task Index() + { + HttpClient client = new HttpClient(); + var userId = $"dl_{Guid.NewGuid()}"; + + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, dlUrl); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secret); + request.Content = new StringContent( + JsonConvert.SerializeObject( + new { User = new { Id = userId } }), + Encoding.UTF8, + "application/json"); + + var response = await client.SendAsync(request); + + string token = String.Empty; + if (response.IsSuccessStatusCode) + { + var body = await response.Content.ReadAsStringAsync(); + token = JsonConvert.DeserializeObject(body).token; + } + + var config = new ChatConfig() + { + Token = token, + UserId = userId + }; + + return View(config); + } + } + + ``` + + The generated token, based on the Direct Line secret, is then used in the Web Chat control as shown below: + + ```csharp + @model Bot_Auth_DL_Secure_Site_MVC.Models.ChatConfig + @{ + ViewData["Title"] = "Home Page"; + } +
+ + + + + + + + ``` + +## Identity spoofing + +Identity spoofing refers to the action of an attacker that assumes the identity of a legitimate user and then uses that identity to accomplish a malicious goal. + +When a bot asks the channel user A to sign-in to an identity provider, the sign-in process must assure that user A is the only one that signs into the provider. If another user is also allowed to sign-in the provider, they would have access to user A resources through the bot. + +### User identity spoofing mitigation + +In the Web Chat control, there are two mechanisms to assure that the proper user is signed in. + +1. **Magic code**. At the end of the sign-in process, the user is presented with a randomly generated 6-digit code (_magic code_). The user must type this code in the conversation to complete the sign-in process. This tends to result in a bad user experience. Additionally, it's susceptible to phishing attacks; a malicious user can trick another user to sign-in and obtain the magic code. + +1. **Direct Line enhanced authentication**. Use Direct Line enhanced authentication to guarantee that the sign-in process can only be completed in the _same browser session_ as the Web Chat client. + + To enable this protection, start Web Chat with a Direct Line token that contains a list of _trusted domains that can host the bot's Web Chat client_. With enhanced authentication options, you can statically specify the trusted domains (trusted origins) list in the Direct Line configuration page. See the [Configure enhanced authentication](../bot-service-channel-connect-directline.md#configure-enhanced-authentication) section. diff --git a/articles/v4sdk/bot-builder-security-guidelines.md b/articles/v4sdk/bot-builder-security-guidelines.md new file mode 100644 index 000000000..83c1e1a9d --- /dev/null +++ b/articles/v4sdk/bot-builder-security-guidelines.md @@ -0,0 +1,116 @@ +--- +title: Bot Framework security guidelines - Bot Service +description: Learn about the security guidelines in the Bot Framework. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: article +ms.service: azure-ai-bot-service +ms.custom: + - evergreen +--- + +# Bot Framework security guidelines + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +Bots are more and more prevalent in key business areas like financial services, retail, travel, and so on. A bot might collect very sensitive data such as credit cards, SSN, bank accounts, and other personal information. So, it's important that bots are secure and protect against common threats and vulnerabilities. + +You can take some standard preventative measures to improve your bot's security. Some security measures are similar to the ones used in other software systems, while some are specific to the Bot Framework. For the latter, refer to the [Azure Security Benchmark](../security-baseline.md). The benchmark provides recommendations on how you can secure your cloud solutions on Azure. + +## Security issues in a nutshell + +This article groups security issues into 2 categories: + +- **Threats**: The tactics someone might use to compromise your bot, such as spoofing, tampering, disclosing information, denial of service, and so on. + +- **Vulnerabilities**: The ways in which your bot or the management of your bot might be susceptible to such tactics, such as bugs, or lax security. + +Reducing your vulnerabilities is a good way to mitigate threats, and a known way to reduce vulnerabilities is to implement security check points in the development and deployment process. + +## Common security guidelines + +The following areas are covered by standard security best practices common to applications. + +### Securing network traffic + +Protocols exist that provide cryptographic controls to address data tampering and disclosure during transmission. In this regard, bots should communicate only over secured channels. + +To exchange data on the wire any secure system must use the **HTTPS** protocol, which transfers data over HTTP in encrypted connections protected by [Transport Layer Security](https://tools.ietf.org/html/rfc5246) (TLS) or [Secure Sockets Layer](https://tools.ietf.org/html/rfc6101) (SSL). See also [RFC 2818 - HTTP Over TLS](https://tools.ietf.org/html/rfc2818). + +> [!NOTE] +> All Bot Service channels require **HTTPS** and **TLS 1.2**. + +### Self-destructing messages + +Permanently delete any sensitive data as soon as it's no longer needed, usually after the message exchange ends, or after a certain amount of time. This can include personally-identifying information, IDs, PINs, passwords, security questions and answers, and so so. + +### Data storage + +The best practice calls for storing information in a secure state for a certain amount of time and then discarding it later after it served its purpose. + +Some common security techniques are listed below. + +#### Database firewalls + +- Firewalls deny access to traffic by default. The only traffic allowed should originate from specific applications or web servers that need to access the data. +- You should also deploy a web application firewall. This is because attacks such as SQL injection attacks directed at a web application can be used to exfiltrate or delete data from the database. + +#### Database hardening + +- Make sure that the database is still supported by the vendor, and you're running the latest version of the database with all the security patches installed to remove known vulnerabilities. +- Uninstall or disable any features or services that you don't need and make sure you change the passwords of any default accounts from their default values; or better, delete any default accounts that you don't need. +- Make sure that all the database security controls provided by the database are enabled, unless there is a specific reason for any to be disabled. + +#### Minimize valuable information + +- Make sure that you're not storing any confidential information that doesn't need to be in the database. +- Data retained for compliance or other purposes can be moved to more secure storage, perhaps offline, which is less susceptible to database security threats. +- Make sure to delete any history files that are written by a server during the original installation procedure. If the installation is successful these files have no value but can contain information that can potentially be exploited. + +### Education + +Bots provide an innovative interaction tool between a company and its customers. But they could potentially provide a backdoor for tampering with a company's website. Therefore, a company must assure that its developers understand the importance of bot security as part of the website security. Moreover, users' errors can be a problem, too. This will require some education on how bots can be used securely, for example: + +- For the developers, a strategy should include internal training on how to use the bot securely. +- Customers can be given guidelines detailing how to interact with the bot safely. + +## Bot-specific security guidelines + +The following areas are covered by some standard security best practices for Bot Framework applications. +The following guidelines describe the Bot Framework best practice security measures. For more information, see the [Security and Privacy FAQ](~/bot-service-resources-faq-security.md). + +### Bot Connector authentication + +The Bot Connector service natively uses HTTPS to exchange messages between a bot and channels (users). the Bot Framework SDK automates basic bot-to-channel authentication for you. + +> [!WARNING] +> If you're writing your own authentication code, it's critical that you implement all security procedures correctly. By implementing all steps described in the [Authentication](~/rest-api/bot-framework-rest-connector-authentication.md) article, you can mitigate the risk of an attacker being able to read messages that are sent to your bot, send messages that impersonate your bot, and steal secret keys. + +### User authentication + +**Azure AI Bot Service authentication** enables you to authenticate users to and get **access tokens** from various identity providers such as *Microsoft Entra ID*, *GitHub*, *Uber* and so on. You can also configure authentication for a custom **OAuth2** identity provider. All this enables you to write **one piece of authentication code** that works across all supported identity providers and channels. To utilize these capabilities you need to perform the following steps: + +1. Statically configure `settings` on your bot that contains the details of your application registration with an identity provider. +1. Use an `OAuthCard`, backed by the application information you supplied in the previous step, to sign-in a user. +1. Retrieve access tokens through **Azure AI Bot Service API**. A good practice is to place a time limit on how long an authenticated user can stay *logged in*. + +For more information, see the [User authentication](~/v4sdk/bot-builder-concept-authentication.md) article. + +### Web Chat + +When you use the [Web Chat](~/bot-service-channel-connect-webchat.md) control you must keep in mind some important security considerations about impersonation and identity spoofing. For more information, see [Direct Line enhanced authentication](bot-builder-security-enhanced.md). + +## Additional information + +- [User authentication](~/v4sdk/bot-builder-concept-authentication.md) +- [Add authentication to your bot via Azure AI Bot Service](~/v4sdk/bot-builder-authentication.md) +- [Enable security and test on localhost](~/bot-service-troubleshoot-authentication-problems.md#step-3-enable-security-and-test-on-localhost) +- [Secrets and tokens](~/rest-api/bot-framework-rest-direct-line-3-0-authentication.md#secrets-and-tokens) +- [Authentication technologies](~/rest-api/bot-framework-rest-connector-authentication.md#authentication-technologies) +- [Enhanced Direct Line Authentication Features](https://blog.botframework.com/2018/09/25/enhanced-direct-line-authentication-features) +- [Security recommendations in Azure Security Center](/azure/security-center/security-center-recommendations) +- [Threat protection in Azure Security Center](/azure/security-center/threat-protection) +- [Azure Security Center Data Security](/azure/security-center/security-center-data-security) +- [Container security in Security Center](/azure/security-center/container-security) diff --git a/articles/v4sdk/bot-builder-send-welcome-message.md b/articles/v4sdk/bot-builder-send-welcome-message.md index f0970996b..b5d374b4c 100644 --- a/articles/v4sdk/bot-builder-send-welcome-message.md +++ b/articles/v4sdk/bot-builder-send-welcome-message.md @@ -1,52 +1,57 @@ --- -title: Send welcome message to users - Bot Service -description: Learn how to develop your bot to provide a welcoming user experience. -keywords: overview, develop, user experience, welcome, personalized experience, C#, JS, welcome message, bot, greet, greeting -author: DanDev33 -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 2/7/2020 +title: Send welcome message to users +description: Find out how to engage users in meaningful conversations with bots by learning about welcome messages. See code samples that detect and greet new users. +keywords: overview, develop, user experience, welcome, personalized experience, C#, JS, welcome message, bot, greet, greeting +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- + # Send welcome message to users -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +The primary goal when creating any bot is to engage your user in a meaningful conversation. One of the best ways to achieve this goal is to ensure that from the moment a user first connects, they understand your bot's main purpose and capabilities, the reason your bot was created. This article provides code examples to help you welcome users to your bot. -The primary goal when creating any bot is to engage your user in a meaningful conversation. One of the best ways to achieve this goal is to ensure that from the moment a user first connects, they understand your bot’s main purpose and capabilities, the reason your bot was created. This article provides code examples to help you welcome users to your bot. +[!INCLUDE [java-python-sunset-alert](../includes/java-python-sunset-alert.md)] ## Prerequisites - Understand [bot basics](bot-builder-basics.md). -- A copy of the **Welcome user sample** in either [C# Sample](https://aka.ms/welcome-user-mvc), [JS Sample](https://aka.ms/bot-welcome-sample-js) or [Python Sample](https://aka.ms/bot-welcome-python-sample-code). The code from the sample is used to explain how to send welcome messages. +- A copy of the **Welcome user sample** in either [C# Sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/03.welcome-user), [JS Sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/03.welcome-users), [Java Sample](https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/java_springboot/03.welcome-user) or [Python Sample](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/python/03.welcome-user). The code from the sample is used to explain how to send welcome messages. ## About this sample code -This sample code shows how to detect and welcome new users when they are initially connected to your bot. The following diagram shows the logic flow for this bot. +This sample code shows how to detect and welcome new users when they're initially connected to your bot. The following diagram shows the logic flow for this bot. ### [C#](#tab/csharp) The two main events encountered by the bot are: -- `OnMembersAddedAsync` which is called whenever a new user is connected to your bot -- `OnMessageActivityAsync` which is called whenever a new user input is received. +- `OnMembersAddedAsync`, called when a new user connects to your bot. +- `OnMessageActivityAsync`, called when your bot receives new user input. -![welcome user logic flow](media/welcome-user-flow.png) +:::image type="content" source="./media/welcome-user-flow.png" alt-text="Logic flow diagram for C# sample."::: -Whenever a new user is connected, they are provided with a `WelcomeMessage`, `InfoMessage`, and `PatternMessage` by the bot. +Whenever a new user is connected, they're provided with a `WelcomeMessage`, `InfoMessage`, and `PatternMessage` by the bot. When a new user input is received, WelcomeUserState is checked to see if `DidBotWelcomeUser` is set to _true_. If not, an initial welcome user message is returned to the user. ### [JavaScript](#tab/javascript) The two main events encountered by the bot are: -- `onMembersAdded` which is called whenever a new user is connected to your bot -- `onMessage` which is called whenever a new user input is received. +- `onMembersAdded`, called when a new user connects to your bot. +- `onMessage`, called when your bot receives new user input. -![welcome user logic flow](media/welcome-user-flow-js.png) +:::image type="content" source="./media/welcome-user-flow-js.png" alt-text="Logic flow diagram for JavaScript sample."::: -Whenever a new user is connected, they are provided with a `welcomeMessage`, `infoMessage`, and `patternMessage` by the bot. +Whenever a new user is connected, they're provided with a `welcomeMessage`, `infoMessage`, and `patternMessage` by the bot. When a new user input is received, `welcomedUserProperty` is checked to see if `didBotWelcomeUser` is set to _true_. If not, an initial welcome user message is returned to the user. If `DidBotWelcomeUser` is _true_, the user's input is evaluated. Based on the content of the user's input this bot will do one of the following: @@ -54,17 +59,29 @@ If `DidBotWelcomeUser` is _true_, the user's input is evaluated. Based on the co - Display a hero card providing addition information about bots. - Resend the `WelcomeMessage` explaining expected inputs for this bot. +### [Java](#tab/java) + +The two main events encountered by the bot are: + +- `onMembersAdded`, called when a new user connects to your bot. +- `onMessageActivity`, called when your bot receives new user input. + +:::image type="content" source="./media/welcome-user-flow-java.png" alt-text="Logic flow diagram for Java sample."::: + +Whenever a new user is connected, they're provided with a `WELCOME_MESSAGE`, `INFO_MESSAGE`, and `PATTERN_MESSAGE` by the bot. +When a new user input is received, WelcomeUserState is checked to see if `getDidBotWelcomeUser()` is set to _true_. If not, an initial welcome user message is returned to the user. + ### [Python](#tab/python) The two main events encountered by the bot are: -- `on_members_added_activity` which is called whenever a new user is connected to your bot -- `on_message_activity` which is called whenever a new user input is received. +- `on_members_added_activity`, called when a new user connects to your bot. +- `on_message_activity`, called when your bot receives new user input. -![welcome user logic flow](media/welcome-user-flow-python.png) +:::image type="content" source="./media/welcome-user-flow-python.png" alt-text="Logic flow diagram for Python sample."::: -Whenever a new user is connected, they are provided with a *welcome message*, *information message*, and a *pattern message* by the bot. -When a new user input is received, the `welcome_user_state.did_welcome_user` property is checked to see if it is set to *true*. If it is not set to *true*, an initial welcome user message is returned to the user. If it is set to *true*, based on the content of the user's input this bot will do one of the following: +Whenever a new user is connected, they're provided with a _welcome message_, _information message_, and a _pattern message_ by the bot. +When a new user input is received, the `welcome_user_state.did_welcome_user` property is checked. If it isn't set to _true_, an initial welcome user message is returned to the user. If it's set to _true_, based on the content of the user's input this bot will do one of the following: - Echo back a greeting received from the user. - Display a hero card providing addition information about bots. @@ -78,30 +95,35 @@ When a new user input is received, the `welcome_user_state.did_welcome_user` pro The user state object is created at startup and dependency injected into the bot constructor. **Startup.cs** -[!code-csharp[define state](~/../botBuilder-samples/samples/csharp_dotnetcore/03.welcome-user/Startup.cs?range=30-34)] +[!code-csharp[define state](~/../botBuilder-samples/samples/csharp_dotnetcore/03.welcome-user/Startup.cs?range=23-27)] **Bots\WelcomeUserBot.cs** -[!code-csharp[consume state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=41-47)] +[!code-csharp[consume state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=46-52)] ### [JavaScript](#tab/javascript) At startup, user state is defined in `index.js` and consumed by the bot constructor. **index.js** -[!code-javascript[define state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/index.js?range=51-55)] +[!code-javascript[define state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/index.js?range=62-69)] -**bots/welcomeBot.js** -[!code-javascript[consume state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomeBot.js?range=16-22)] +### [Java](#tab/java) + +The user state object is created at startup and dependency injected into the bot constructor by the Spring container. + +**Application.java** +[!code-java[define state](~/../botBuilder-samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/Application.java?range=50-53)] + +**WelcomeUserBot.java** +[!code-java[consume state](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=80-86)] ### [Python](#tab/python) At startup, user state is defined in `app.py` and consumed by the bot constructor. **app.py** -[!code-python[define state](~/../botbuilder-samples/samples/python/03.welcome-user/app.py?range=61-66)] -**bots/welcome-user-bot.py** -[!code-python[consume state](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=23-29)] +[!code-python[define state](~/../botbuilder-samples/samples/python/03.welcome-user/app.py?range=62-67)] --- @@ -112,17 +134,31 @@ At startup, user state is defined in `app.py` and consumed by the bot constructo We now create a property accessor that provides us a handle to `WelcomeUserState` inside the `OnMessageActivityAsync` method. Then call the `GetAsync` method to get the properly scoped key. We then save user state data after each user input iteration using the `SaveChangesAsync` method. +**Bots\WelcomeUserState.cs** + +[!code-csharp[Get state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/WelcomeUserState.cs?range=12-13)] + **Bots\WelcomeUserBot.cs** -[!code-csharp[Get state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=68-71)] -[!code-csharp[Save state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range= 103-105)] +[!code-csharp[Await state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=75-76)] +[!code-csharp[Save state](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range= 108-109)] ### [JavaScript](#tab/javascript) We now create a property accessor that provides us a handle to welcomedUserProperty which is persisted within userState. **bots/welcomeBot.js** -[!code-javascript[Get state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=24-27)] -[!code-javascript[Save state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=89-97)] +[!code-javascript[Get state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=20)] +[!code-javascript[Await state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=24-27)] +[!code-javascript[Save state](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=93-101)] + +### [Java](#tab/java) + +We now create a property accessor that provides us a handle to `WelcomeUserState` inside the `onMessageActivity` method. +Then call the `get` method to get the properly scoped key. We then save user state data after each user input iteration using the `saveChanges` method. + +**WelcomeUserBot.java** +[!code-java[Get state](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=151-155)] +[!code-java[Save state](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range= 98-99)] ### [Python](#tab/python) @@ -133,8 +169,8 @@ This sample creates a conversation state property accessor, `user_state_accessor It uses the property accessor in the `on_message_activity` handler and overrides the `on_turn` handler to save state before the end of the turn. -[!code-python[Get state](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=80-83)] -[!code-python[save state](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=39-43)] +[!code-python[Get state](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=88-91)] +[!code-python[save state](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=54-58)] --- @@ -146,21 +182,29 @@ In **WelcomeUserBot**, we check for an activity update using `OnMembersAddedAsyn **Bots\WelcomeUserBot.cs** [!code-csharp[Define messages](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=20-39)] -[!code-csharp[Send messages](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=55-66)] +[!code-csharp[Send messages](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=59-71)] ### [JavaScript](#tab/javascript) This JavaScript code sends initial welcome messages when a user is added. This is done by checking the conversation activity and verifying that a new member was added to the conversation. **bots/welcomeBot.js** -[!code-javascript[onMembersAdded](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=63-86)] +[!code-javascript[onMembersAdded](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=63-90)] + +### [Java](#tab/java) + +In **WelcomeUserBot**, we check for an activity update using `onMembersAdded()` to see if a new user has been added to the conversation and then send them a set of three initial welcome messages `WELCOME_MESSAGE`, `INFO_MESSAGE` and `PATTERN_MESSAGE`. Complete code for this interaction is shown below. + +**WelcomeUserBot.java** +[!code-java[Define messages](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=47-72)] +[!code-java[Send messages](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=115-140)] ### [Python](#tab/python) -The `on_members_added_activity` checks to see if a new user has been added and then sends three initial welcome messages: a *welcome message*, an *information message* and a *pattern message*. +The `on_members_added_activity` checks to see if a new user has been added and then sends three initial welcome messages: a _welcome message_, an _information message_ and a _pattern message_. **bots/welcome-user-bot.py** -[!code-python[on_members_added_activity](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=45-74)] +[!code-python[on_members_added_activity](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=63-82)] --- @@ -168,39 +212,47 @@ The `on_members_added_activity` checks to see if a new user has been added and t ### [C#](#tab/csharp) -It is also important to consider when your user’s input might actually contain useful information, and this may vary for each channel. To ensure your user has a good experience on all possible channels, we check the status flag _didBotWelcomeUser_ and if this is "false", we do not process the initial user input. We instead provide the user with an initial welcome message. The bool _welcomedUserProperty_ is then set to "true", stored in UserState and our code will now process this user's input from all additional message activities. +It's also important to consider when your user's input might actually contain useful information, and this may vary for each channel. To ensure your user has a good experience on all possible channels, we check the status flag _didBotWelcomeUser_ and if this is "false", we don't process the initial user input. We instead provide the user with an initial welcome message. The bool _welcomedUserProperty_ is then set to "true", stored in UserState and our code will now process this user's input from all additional message activities. **Bots\WelcomeUserBot.cs** -[!code-csharp[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=68-84)] -[!code-csharp[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=101-105)] +[!code-csharp[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=73-87)] +[!code-csharp[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=108-109)] ### [JavaScript](#tab/javascript) -It is also important to consider when your user’s input might actually contain useful information, and this may vary for each channel. To ensure your user has a good experience on all possible channels, we check the didBotWelcomedUser property, if it does not exist, we set it to "false" and do not process the initial user input. We instead provide the user with an initial welcome message. The bool didBotWelcomeUser is then set to "true" and our code processes the user input from all additional message activities. +It's also important to consider when your user's input might actually contain useful information, and this may vary for each channel. To ensure your user has a good experience on all possible channels, we check the didBotWelcomedUser property, if it doesn't exist, we set it to "false" and don't process the initial user input. We instead provide the user with an initial welcome message. The bool didBotWelcomeUser is then set to "true" and our code processes the user input from all additional message activities. **bots/welcomeBot.js** [!code-javascript[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=24-39)] [!code-javascript[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=57-61)] +### [Java](#tab/java) + +It's important to consider when your user's input might contain useful information, which may vary for each channel. To ensure your user has a good experience on all possible channels, we check the status flag _getDidBotWelcomeUser_ and if this is "false", we don't process the initial user input. We instead provide the user with an initial welcome message. The bool _setDidBotWelcomeUser_ is then set to "true", stored in UserState and our code will now process this user's input from all additional message activities. + +**WelcomeUserBot.java** +[!code-java[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=149-167)] +[!code-java[DidBotWelcomeUser](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=186-187)] + ### [Python](#tab/python) -It is also important to consider when the user’s input might actually contain useful information, this may vary for each channel. To ensure the user has a good experience on all possible channels, `on_message_activity` checks the `did_welcome_user` property. The first time, it sets it to *false* and does not process the user input. Instead, it provides the user with an initial welcome message. Then it sets `did_welcome_user` to *true* and processes the user input from all additional message activities. +It's also important to consider when the user's input might actually contain useful information, this may vary for each channel. To ensure the user has a good experience on all possible channels, `on_message_activity` checks the `did_welcome_user` property. The first time, it sets it to _false_ and doesn't process the user input. Instead, it provides the user with an initial welcome message. Then it sets `did_welcome_user` to _true_ and processes the user input from all additional message activities. **bots/welcome-user-bot.py** -[!code-python[DidBotWelcomeUser](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=85-95)] +[!code-python[DidBotWelcomeUser](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=93-103)] --- ## Process additional input -Once a new user has been welcomed, user input information is evaluated for each message turn and the bot provides a response based on the context of that user input. The following code shows the decision logic used to generate that response. +Once a new user has been welcomed, user input information is evaluated for each message turn, and the bot provides a response based on the context of that user input. The following code shows the decision logic used to generate that response. ### [C#](#tab/csharp) An input of 'intro' or 'help' calls the function `SendIntroCardAsync` to present the user with an informational hero card. That code is examined in the next section of this article. **Bots\WelcomeUserBot.cs** -[!code-csharp[SwitchOnUtterance](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=85-100)] +[!code-csharp[SwitchOnUtterance](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=91-105)] ### [JavaScript](#tab/javascript) @@ -209,48 +261,63 @@ An input of 'intro' or 'help' uses CardFactory to present the user with an Intro **bots/welcomeBot.js** [!code-javascript[SwitchOnUtterance](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=40-56)] +### [Java](#tab/java) + +An input of 'intro' or 'help' calls the function `sendIntroCard` to present the user with an informational hero card. That code is examined in the next section of this article. + +**WelcomeUserBot.java** +[!code-java[SwitchOnUtterance](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=169-183)] + ### [Python](#tab/python) -An user's input of *intro* or *help* causes the bot to call `__send_intro_card` which presents the user with an intro adaptive card. +A user's input of _intro_ or _help_ causes the bot to call `__send_intro_card`, which presents the user with an intro adaptive card. **bots/welcome-user-bot.py** -[!code-python[SwitchOnUtterance](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=101-106&highlight=97-106)] +[!code-python[SwitchOnUtterance](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=109-114&highlight=97-106)] --- ## Using hero card greeting -As mentioned above, some user inputs generate a *Hero Card* in response to their request. You can learn more about hero card greetings here [Send an Intro Card](./bot-builder-howto-add-media-attachments.md). Below is the code required to create this bot's hero card response. +As mentioned above, some user inputs generate a _Hero Card_ in response to their request. You can learn more about hero card greetings here [Send an Intro Card](./bot-builder-howto-add-media-attachments.md). Below is the code required to create this bot's hero card response. ### [C#](#tab/csharp) **Bots\WelcomeUserBot.cs** -[!code-csharp[SendHeroCardGreeting](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=107-125)] +[!code-csharp[SendHeroCardGreeting](~/../BotBuilder-Samples/samples/csharp_dotnetcore/03.welcome-user/bots/WelcomeUserBot.cs?range=112-132)] ### [JavaScript](#tab/javascript) **bots/welcomeBot.js** -[!code-javascript[SendIntroCard](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=99-124)] +[!code-javascript[SendIntroCard](~/../BotBuilder-Samples/samples/javascript_nodejs/03.welcome-users/bots/welcomebot.js?range=103-128)] + +### [Java](#tab/java) + +**WelcomeUserBot.java** +[!code-java[SendHeroCardGreeting](~/../BotBuilder-Samples/samples/java_springboot/03.welcome-user/src/main/java/com/microsoft/bot/sample/welcomeuser/WelcomeUserBot.java?range=192-235)] ### [Python](#tab/python) **bots/welcome-user-bot.py** -[!code-python[SendIntroCard](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=108-143)] +[!code-python[SendIntroCard](~/../botbuilder-samples/samples/python/03.welcome-user/bots/welcome_user_bot.py?range=116-151)] --- ## Test the bot -Download and install the latest [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) +Download and install the latest [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) -1. Run the sample locally on your machine. If you need instructions, refer to the README file for [C# Sample](https://aka.ms/welcome-user-mvc) or [JS Sample](https://aka.ms/bot-welcome-sample-js). -1. Use the emulator to test the bot as shown below. +1. Run the sample locally on your machine. If you need instructions, refer to the `README` file for the [C# sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/03.welcome-user) or [JavaScript sample](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/03.welcome-users). +1. Open the Emulator to test your bot. + 1. When you start a conversation with your bot, it will send you a series of welcome messages. + 1. When you send a "hello" message the first time, your bot replies with some advice. + 1. When you send subsequent "hello" messages, your bot replies with, "You said hello." -![test welcome bot sample](media/welcome-user-emulator-1.png) + :::image type="content" source="media/welcome-user-emulator-1.png" alt-text="Screenshot of initial interactions with your bot in the Emulator."::: -Test hero card greeting. + 1. Send a "help" message to your bot. It responds by sending a hero card. -![test welcome bot card](media/welcome-user-emulator-2.png) + :::image type="content" source="media/welcome-user-emulator-2.png" alt-text="Screenshot of the help message and the bot response in the Emulator."::: ## Additional Resources diff --git a/articles/v4sdk/bot-builder-service-file.md b/articles/v4sdk/bot-builder-service-file.md deleted file mode 100644 index 71f6bd5ca..000000000 --- a/articles/v4sdk/bot-builder-service-file.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Create a bot using Bot Framework SDK for JavaScript - File - Bot Service -description: Quickly create a bot using the Bot Framework SDK for JavaScript. -keywords: quickstart, bot framework sdk, getting started -author: jonathanfingold -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- -# Sample .bot file - -``` -{ - "name": "BasicBot", - "description": "Demo", - "services": [ - { - "type": "endpoint", - "name": "development", - "id": "http://localhost:3978/api/messages", - "appId": "", - "appPassword": "", - "endpoint": "http://localhost:3978/api/messages" - }, - { - "type": "luis", - "name": "basic-bot-LUIS", - "appId": "", - "version": "0.1", - "authoringKey": "", - "subscriptionKey": "", - "region": "westus", - "id": "206" - } - ], - "secretKey": "", - "version": "2.0" -} -``` diff --git a/articles/v4sdk/bot-builder-skills-overview.md b/articles/v4sdk/bot-builder-skills-overview.md deleted file mode 100644 index d1e29909a..000000000 --- a/articles/v4sdk/bot-builder-skills-overview.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Bot Framework Skills Overview - Bot Service -description: Learn more about the Bot Framework Skills -author: darrenj -ms.author: darrenj -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 10/09/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Virtual Assistant - Skills Overview - -> [!NOTE] -> This topic applies to v4 version of the SDK. - -## Overview - -Developers can compose conversational experiences by stitching together re-usable conversational capabilities, known as Skills. - -Within an Enterprise, this could be creating one parent bot bringing together multiple sub-bots owned by different teams, or more broadly leveraging common capabilities provided by other developers. With this preview of Skills, developers can create a new bot (typically through the Virtual Assistant template) and add/remove Skills with one command line operation incorporating all Dispatch and Configuration changes. - -Skills are themselves Bots, invoked remotely and a Skill developer template (.NET, TS) is available to facilitate creation of new Skills. - -A key design goal for Skills was to maintain the consistent Activity protocol and ensure the development experience was as close to any normal V4 SDK bot as possible. - -![Skills Scenarios](./media/enterprise-template/skills-scenarios.png) - -## Bot Framework Skills - -At this time we have made available the following Bot Framework Skills, powered by the Microsoft Graph and available in multiple languages. - -![Skills Scenarios](./media/enterprise-template/skills-at-build.png) - -| Name | Description | -| ---- | ----------- | -|[Calendar Skill](https://aka.ms/bf-calendar-skill)|Add calendar capabilities to your assistant. Powered by Microsoft Graph and Google.| -|[Email Skill](https://aka.ms/bf-email-skill)|Add email capabilities to your assistant. Powered by Microsoft Graph and Google.| -|[To Do Skill](https://aka.ms/bf-todo-skill)|Add task management capabilities to your assistant. Powered by Microsoft Graph.| -|[Point of Interest Skill](https://aka.ms/bf-poi-skill)|Find points of interest and directions. Powered by Azure Maps and FourSquare.| -|[Automotive Skill](https://aka.ms/bf-auto-skill)|Industry-vertical Skill for showcasing enabling car feature control.| -|[Experimental Skills](https://aka.ms/bf-experimental-skills)|News, Restaurant Booking and Weather.| - -## Getting Started - -Refer to the [tutorials](https://aka.ms/bfs-tutorials) to learn how to leverage existing skills and build your own. diff --git a/articles/v4sdk/bot-builder-telemetry-analytics-queries.md b/articles/v4sdk/bot-builder-telemetry-analytics-queries.md index a3e156517..a8a1aa798 100644 --- a/articles/v4sdk/bot-builder-telemetry-analytics-queries.md +++ b/articles/v4sdk/bot-builder-telemetry-analytics-queries.md @@ -1,50 +1,58 @@ --- -title: Analyze the telemetry data from your bot - Bot Service +title: Analyze the telemetry data from your bot description: Learn how to analyze bot behavior with Kusto queries. keywords: telemetry, appinsights, monitor bot, Kusto, queries -author: WashingtonKayaker -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/10/2020 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Analyze your bot's telemetry data +# Analyze your bot's telemetry data + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] ## Analyzing Bot behavior -The following collection of queries can be used to analyze bot behavior. You can use the collection to author custom queries in [Azure Monitor Log Analytics](https://aka.ms/log-analytics-azure-monitor) and to create monitoring and [PowerBI](https://aka.ms/power-bi-overview) visualization dashboards. +The following collection of queries can be used to analyze bot behavior. You can use the collection to author custom queries in [Azure Monitor Log Analytics](/azure/azure-monitor/log-query/log-analytics-tutorial) and to create monitoring and [Power BI](/power-bi/fundamentals/power-bi-overview) visualization dashboards. ## Prerequisites -It is helpful to have a basic understanding of the following concepts: -* [Kusto queries](https://aka.ms/Kusto-query-overview) +It's helpful to have a basic understanding of the following concepts: -* How to use [Log Analytics](https://aka.ms/azure-monitor-log-queries-get-started) in the Azure portal to write Azure Monitor log queries +* [Kusto queries](/azure/data-explorer/kusto/query/) +* How to use [Log Analytics](/azure/azure-monitor/log-query/get-started-queries) in the Azure portal to write Azure Monitor log queries +* The basic concepts of [Log queries](/azure/azure-monitor/log-query/get-started-queries) in Azure Monitor -* The basic concepts of [Log queries](https://aka.ms/azure-monitor-log-queries-get-started) in Azure Monitor +> [!TIP] +> If your create your bot using tools such as [Copilot Studio](/microsoft-copilot-studio/fundamentals-what-is-copilot-studio) or [Composer](/composer), you'll want to use the Adaptive Dialog version of each query when available. ## Dashboards -Azure Dashboards offer a great way to view and share the information generated from your queries. You can build custom dashboards to help monitor your bots activity by associating your queries with the tiles that you add to your dashboard. For more information on dashboards and how to associate your queries with them, see [Create and share dashboards of Log Analytics data](https://aka.ms/log-analytics-create-share-dashboards). The remainder of this article shows examples of some of the queries that you may find useful in monitoring your bots behavior. + +Azure Dashboards offer a great way to view and share the information generated from your queries. You can build custom dashboards to help monitor your bots activity by associating your queries with the tiles that you add to your dashboard. For more information on dashboards and how to associate your queries with them, see [Create and share dashboards of Log Analytics data](/azure/azure-monitor/learn/tutorial-logs-dashboards). The remainder of this article shows examples of some of the queries that you may find useful in monitoring your bots behavior. ## Example Kusto queries > [!NOTE] -> It is recommended to pivot on different dimensions such as period, channel, and locale for all queries in this article. +> It's recommended to pivot on different dimensions such as period, channel, and locale for all queries in this article. ### Number of users per period + This example results in a line chart that shows how many distinct users communicated with your bot per day for the past 14 days. The time period can be easily changed by assigning different values to the `queryStartDate`, `queryEndDate` and `interval` variables. > [!IMPORTANT] -> You will only get a correct count of unique users in this query if they are authenticated users, and the results may also depend on the channel capabilities. +> You'll only get a correct count of unique users in this query if they're authenticated users, and the results may also depend on the channel capabilities. ```Kusto // number of users per period let queryStartDate = ago(14d); let queryEndDate = now(); let groupByInterval = 1d; -customEvents +customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate | summarize uc=dcount(user_Id) by bin(timestamp, groupByInterval) @@ -52,83 +60,57 @@ customEvents ``` > [!TIP] -> The Kusto [summarize operator](https://aka.ms/kusto-summarize-operator) is used to produce a table that aggregates the content of the input table. +> The Kusto [summarize operator](/azure/data-explorer/kusto/query/summarizeoperator) is used to produce a table that aggregates the content of the input table. > -> The [Bin](https://docs.microsoft.com/azure/kusto/query/binfunction) function is a Kusto scalar function that when used in conjunction with the `summarize operator` will group the query results into the specified value. In the above example, this is grouped by day, Kusto also will accept h=hours, m=minutes, s=seconds, ms=milliseconds, microsecond=microseconds. +> The [Bin](/azure/kusto/query/binfunction) function is a Kusto scalar function that when used in conjunction with the `summarize operator` will group the query results into the specified value. In the above example, this is grouped by day, Kusto also will accept h=hours, m=minutes, s=seconds, ms=milliseconds, microsecond=microseconds. > -> The [render operator](https://aka.ms/kusto-query-render-operator?pivots=Kusto) enables you to easily render charts, such as the _timechart_, a line chart where the x-axis is a datetime and any other numeric column can be used for the y-axis. It automatically keeps the x-axis spaced nicely even if your data doesn't have every time specified. If no render statement is used, it defaults to `table`. - -#### Sample query results - - - -![Number of users per period](./media/userscount.png) +> The [render operator](/azure/data-explorer/kusto/query/renderoperator) enables you to easily render charts, such as the _timechart_, a line chart where the x-axis is a datetime and any other numeric column can be used for the y-axis. It automatically keeps the x-axis spaced nicely even if your data doesn't have every time specified. If no render statement is used, it defaults to `table`. - +This example illustrates how to measure the volume of activity per desired dimension, such as a count of the number of conversations, dialogs, or messages per day for the past 14 days. The time period can be easily changed by assigning different values to the `querystartdate`, `queryEndDate` and `interval` variables. The desired dimension is defined by the `extend` clause in the following example, `metric` can be set to either _InstanceId_, _DialogId_ or _activityId_. -### Activity per period -This example illustrates how to measure the volume of activity per desired dimension, whether that be a count of the number of conversations, dialogs, or messages per day for the past 14 days. The time period can be easily changed by assigning different values to the `querystartdate`, `queryEndDate` and `interval` variables. The desired dimension is defined by the `extend` clause in the following example, `metric` can be set to either _InstanceId_, _DialogId_ or _ActivityId_. +Assign _metric_ to the dimension that you want to display: -Assign *metric* to the dimension that you want to display: - * *InstanceId* measures the number of [Conversations](https://aka.ms/bot-builder-conversations) - * *DialogId* measures the number of [Dialogs](https://aka.ms/bot-builder-concept-dialog) - * *ActivityId* measures the number of [Messages](https://aka.ms/bot-rest-create-messages) +* _InstanceId_ measures the number of [Conversations](../bot-service-design-conversation-flow.md) +* _DialogId_ measures the number of [Dialogs](bot-builder-concept-dialog.md) +* _ActivityId_ measures the number of [Messages](../rest-api/bot-framework-rest-connector-create-messages.md) ```Kusto -// measures the number of activity's (conversations, dialogs, messages) per period +// Measures the number of activity's (conversations, dialogs, messages) per period. let queryStartDate = ago(14d); let queryEndDate = now(); let groupByInterval = 1d; -customEvents +customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate -| extend InstanceId = tostring(customDimensions['']) -| extend DialogId = tostring(customDimensions['']) -| extend ActivityId = tostring(customDimensions['']) +| extend InstanceId = tostring(customDimensions['InstanceId']) +| extend DialogId = tostring(customDimensions['DialogId']) +| extend ActivityId = tostring(customDimensions['activityId']) | where DialogId != '' and InstanceId != '' and user_Id != '' | extend metric = InstanceId // DialogId or ActivityId | summarize Count=dcount(metric) by bin(timestamp, groupByInterval) -| order by Count desc nulls last +| order by Count desc nulls last | render timechart ``` > [!TIP] -> The Kusto [extend operator](https://aka.ms/kusto-extend-operator) is used to create calculated columns and append them to the result set. - +> The Kusto [extend operator](/azure/data-explorer/kusto/query/extendoperator) is used to create calculated columns and append them to the result set. -#### Sample query results - -![Activity per period](./media/convscount.PNG) +#### Sample activity-per-period query results +:::image type="content" source="./media/convscount.PNG" alt-text="Sample chart of activity per period."::: ### Activity per user per period -This example demonstrates how to count the number of activities per user per period. This demonstrates drilling down in the _activity per period_ query to focus on the activity per user per period. The activities include dialogs, conversations or messages. This helps to measure user interaction with your bot and can help in determining potential problems, for example: -- Days with lots of activity by a single user may mean attack or test -- Days with little interaction may indicate service health issues +This example demonstrates how to count the number of activities per user per period. This query drills down into the _activity per period_ query to focus on the activity per user per period. The activities include dialogs, conversations or messages. This query measures user interaction with your bot, which can help find potential problems, such as: + +* Days with a lot of activity by a single user may mean an attack or test +* Days with little interaction may indicate service health issues > [!TIP] > You can remove _by user_Id_ to get the general bot activity volume which can be pivoted on time and dialogs, messages, or conversations. @@ -138,7 +120,7 @@ This example demonstrates how to count the number of activities per user per per let queryStartDate = ago(14d); let queryEndDate = now(); let interval = 6h; -customEvents +customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate | extend InstanceId = tostring(customDimensions['InstanceId']) @@ -147,10 +129,10 @@ customEvents | where DialogId != '' and InstanceId != '' and user_Id != '' | extend metric = ActivityId // InstanceId // DialogId // or InstanceId for conversation count | summarize Count=dcount(metric) by user_Id, bin(timestamp, groupByInterval) -| order by Count desc nulls last +| order by Count desc nulls last ``` -#### Sample query results +#### Sample activity-per-user-per-period query results | **user_Id** | **timestamp** | **Count** | | ------------- | -------------------- | :------: | @@ -159,12 +141,17 @@ customEvents | User-75f2cc8d | 2019-09-03T00:00:00Z | 13 | | User-3060aada | 2019-09-03T00:00:00Z | 10 | - ### Dialog completion -Once you set the telemetry client for a dialog, the dialog (and its children) will emit some default telemetry data, such as _started_ and _completed_. This example can be used to measure the *completed* dialogs relative to *started* dialogs. If the number of dialogs started is greater than the number completed, some of your users are not completing the dialog flow. This can be used as a starting point in identifying and troubleshooting any potential dialog logic. It can also be used to identify the more popular and less frequented dialogs. + +Once you set the telemetry client for a dialog, the dialog (and its children) will emit some default telemetry data, such as _started_ and _completed_. This example can be used to measure the _completed_ dialogs relative to _started_ dialogs. If the number of dialogs started is greater than the number completed, some of your users aren't completing the dialog flow. You can use this query to help you identify and troubleshoot any potential dialog logic. It can also be used to identify which dialogs are most and least frequently used. + +> [!TIP] +> If your create your bot using tools such as [Copilot Studio](/microsoft-copilot-studio/fundamentals-what-is-copilot-studio) or [Composer](/composer/), you'll want to use the adaptive dialog version of each query. + +#### Waterfall dialog completion ```Kusto -// % Completed Waterfall Dialog: shows completes relative to starts +// % Completed Waterfall Dialog: shows completes relative to starts let queryStartDate = ago(14d); let queryEndDate = now(); customEvents @@ -174,47 +161,48 @@ customEvents | extend DialogId = customDimensions['DialogId'] | extend InstanceId = tostring(customDimensions['InstanceId']) | join kind=leftouter ( - customEvents - | where name=="WaterfallComplete" + customEvents + | where name=="WaterfallComplete" | extend InstanceId = tostring(customDimensions['InstanceId']) - ) on InstanceId + ) on InstanceId | summarize started=countif(name=='WaterfallStart'), completed=countif(name1=='WaterfallComplete') by tostring(DialogId) | where started > 100 // filter for sample // Show starts vs. completes | project tostring(DialogId), started, completed -| order by started desc, completed asc nulls last -| render barchart with (kind=unstacked, xcolumn=DialogId, ycolumns=completed, started, ysplit=axes) +| order by started desc, completed asc nulls last +| render barchart with (kind=unstacked, xcolumn=DialogId, ycolumns=completed, started, ysplit=axes) ``` > [!TIP] -> The Kusto [join operator](https://aka.ms/kusto-join-operator) is used to merge the rows of two tables to form a new table by matching values of the specified column(s) from each table. +> The Kusto [join operator](/azure/data-explorer/kusto/query/joinoperator) is used to merge the rows of two tables to form a new table by matching values of the specified column(s) from each table. > -> The [project operator](https://aka.ms/kusto-project-operator) is used to select the fields that you want to show up in your output. Similar to the `extend operator` that adds a new field, the `project operator` can either choose from the existing set of fields or add a new field. - -#### Sample query results - -![Dialog completion](./media/dialogwfratio.PNG) - - -### Dialog incompletion -This example can be used to count the number of dialog flows that started but never completed due to cancellation or abandonment during the specified time period. You can use it to review incomplete dialogs and examine whether they were actively cancelled due to user confusion or simply abandoned due to user distraction or loss of interest. +> The [project operator](/azure/data-explorer/kusto/query/projectoperator) is used to select the fields that you want to show up in your output. Similar to the `extend operator` that adds a new field, the `project operator` can either choose from the existing set of fields or add a new field. - +#### Waterfall dialogs not completed ```Kusto -// show incomplete dialogs +// Show incomplete dialogs when using waterfall dialogs. let queryStartDate = ago(14d); let queryEndDate = now(); customEvents @@ -233,24 +221,39 @@ customEvents | render barchart ``` -> [!TIP] -> The Kusto [order operator](https://aka.ms/kusto-query-order-operator) (Same as the `sort operator`) is used to sort the rows of the input table into order by one or more columns. Note: If you want to exclude null values from the results of any query, you can filter them out in your where statement, for example you could add "and isnotnull(Timestamp)", or to return null values at the beginning or end, add the `nulls first` or `nulls first` to the end of the order statement. -> - -#### Sample query results - -![](./media/cancelleddialogs.PNG) +#### Adaptive dialogs not completed +```Kusto +// Show incomplete dialogs for adaptive dialogs; this type is the default dialog type when using Copilot Studio or Composer. +let queryStartDate = ago(14d); +let queryEndDate = now(); +customEvents +| where name == "AdaptiveDialogStart" +| extend DialogId = tostring(customDimensions['DialogId']) +| join kind=rightanti ( +customEvents +| where name == "AdaptiveDialogComplete" +| extend DialogId = tostring(customDimensions['DialogId']) +) on name, DialogId +| summarize cnt=count() by DialogId +| order by cnt +| render barchart +``` +> [!TIP] +> The Kusto [order operator](/azure/data-explorer/kusto/query/orderoperator) (Same as the `sort operator`) is used to sort the rows of the input table into order by one or more columns. Note: If you want to exclude null values from the results of any query, you can filter them out in your `where` statement, for example you could add "and isnotnull(Timestamp)", or to return null values at the beginning or end, add the `nulls first` or `nulls first` to the end of the order statement. +#### Sample dialog-incompletion query results +:::image type="content" source="./media/cancelleddialogs.PNG" alt-text="Sample summary chart for incomplete dialogs."::: -### Dialog Sequence Drill Down +### Dialog Sequence Drill Down #### Waterfall start/step/complete for dialog in conversation -This example shows the sequence of dialog steps, grouped by conversation (instanceId). This can be useful in determining which steps lead to dialog interruption. -The run this query, enter the value of the desired `DialogId` in place of \ +This example shows the sequence of dialog steps, grouped by conversation (instanceId), which can be useful in determining which steps lead to dialog interruption. + +The run this query, enter the value of the desired `DialogId` in place of \ ```Kusto // Drill down: Show waterfall start/step/complete for specific dialog @@ -264,7 +267,7 @@ customEvents | extend StepName = customDimensions['StepName'] | extend InstanceId = customDimensions['InstanceId'] | where DialogId == dlgid -| project timestamp, name, StepName, InstanceId +| project timestamp, name, StepName, InstanceId | order by tostring(InstanceId), timestamp asc }; // For example see SampleDialogId behavior @@ -272,7 +275,7 @@ DialogActivity("") ``` > [!TIP] -> This query was written using a [query-defined function](https://aka.ms/kusto-user-functions), which is a user-defined function that is defined and used within the scope of a single query, and is defined through a let statement. This query written without the use of the `query-defined function`: +> This query was written using a [query-defined function](/azure/data-explorer/kusto/query/functions/user-defined-functions), which is a user-defined function that is defined and used within the scope of a single query, and is defined through a let statement. This query written without the use of the `query-defined function`: > > ```Kusto > let queryStartDate = ago(14d); @@ -284,36 +287,37 @@ DialogActivity("") > | extend StepName = customDimensions['StepName'] > | extend InstanceId = customDimensions['InstanceId'] > | where DialogId == "" -> | project timestamp, name, StepName, InstanceId +> | project timestamp, name, StepName, InstanceId > | order by tostring(InstanceId), timestamp asc > ``` ##### Sample query results -| **timestamp** | **name** | **StepName** | **InstanceId** | -| ------------------- | -------------------------- | ------------------------------- | --------------- | -| 2019-08-23T20:04... | WaterfallStart | null | ...79c0f03d8701 | -| 2019-08-23T20:04... | WaterfallStep | GetPointOfInterestLocations | ...79c0f03d8701 | -| 2019-08-23T20:04... | WaterfallStep | ProcessPointOfInterestSelection | ...79c0f03d8701 | -| 2019-08-23T20:04... | WaterfallStep | GetRoutesToDestination | ...79c0f03d8701 | -| 2019-08-23T20:05... | WaterfallStep | ResponseToStartRoutePrompt | ...79c0f03d8701 | -| 2019-08-23T20:05... | WaterfallComplete _1_ | null | ...79c0f03d8701 | -| 2019-08-28T23:35... | WaterfallStart | null | ...6ac8b3211b99 | -| 2019-08-28T23:35... | WaterfallStep _2_ | GetPointOfInterestLocations | ...6ac8b3211b99 | -| 2019-08-28T19:41... | WaterfallStart | null | ...8137d76a5cbb | -| 2019-08-28T19:41... | WaterfallStep _2_ | GetPointOfInterestLocations | ...8137d76a5cbb | -| 2019-08-28T19:41... | WaterfallStart | null | ...8137d76a5cbb | +| timestamp | name | StepName | InstanceId | +|---------------------|----------------------------------|---------------------------------|-----------------| +| 2019-08-23T20:04... | WaterfallStart | null | ...79c0f03d8701 | +| 2019-08-23T20:04... | WaterfallStep | GetPointOfInterestLocations | ...79c0f03d8701 | +| 2019-08-23T20:04... | WaterfallStep | ProcessPointOfInterestSelection | ...79c0f03d8701 | +| 2019-08-23T20:04... | WaterfallStep | GetRoutesToDestination | ...79c0f03d8701 | +| 2019-08-23T20:05... | WaterfallStep | ResponseToStartRoutePrompt | ...79c0f03d8701 | +| 2019-08-23T20:05... | WaterfallComplete _1_ | null | ...79c0f03d8701 | +| 2019-08-28T23:35... | WaterfallStart | null | ...6ac8b3211b99 | +| 2019-08-28T23:35... | WaterfallStep _2_ | GetPointOfInterestLocations | ...6ac8b3211b99 | +| 2019-08-28T19:41... | WaterfallStart | null | ...8137d76a5cbb | +| 2019-08-28T19:41... | WaterfallStep _2_ | GetPointOfInterestLocations | ...8137d76a5cbb | +| 2019-08-28T19:41... | WaterfallStart | null | ...8137d76a5cbb | -1 _Completed_ +1 _Completed_ -2 _Abandoned_ +2 _Abandoned_ -_Interpretation: Users seem to abandon the conversation at the GetPointOfInterestLocations step._ +_Interpretation: Users seem to abandon the conversation at the GetPointOfInterestLocations step._ -> [!NOTE] -> Waterfall dialogs execute a sequence (start, multiple steps, complete). If a sequence shows start with no complete, it means the dialog was interrupted either due to user abandoning or canceling the dialog. In this detailed analysis, one can see this behavior (see completed vs. abandoned steps). +> [!NOTE] +> Waterfall dialogs execute a sequence (start, multiple steps, complete). If a sequence shows start with no complete, it means the dialog was interrupted either due to user abandoning or canceling the dialog. In this detailed analysis, one can see this behavior (see completed versus abandoned steps). #### Waterfall start/step/complete/cancel steps aggregate totals + This example shows the aggregate totals of the total number of times that a dialog sequence was started, the combined total number of waterfall steps, how many were successfully completed, how many were canceled and the difference between _WaterfallStart_ and the combined total of _WaterfallComplete_ plus _WaterfallCancel_ will give you the total number abandoned. ```Kusto @@ -333,7 +337,7 @@ customEvents DialogSteps("") ``` -##### Sample query results +##### Sample waterfall-aggregate query results | **name** | **count** | | ----------------- | --------: | @@ -342,45 +346,44 @@ DialogSteps("") | WaterfallComplete | 11 | | WaterfallCancel | 1 | -_Interpretation: Of 21 invocations of dialog sequence, only 11 has completed, 9 were abandoned, and one was cancelled by the user_ - - +_Interpretation: Of 21 invocations of dialog sequence, only 11 has completed, 9 were abandoned, and one was canceled by the user._ ### Average duration in dialog -This example measures the average amount of time users spend in a given dialog. A long time spend in a dialog may suggest opportunities to simplify. + +This example measures the average amount of time users spend in a given dialog. Your bot might benefit from simplifying dialogs that take a user a long time to complete. ```Kusto // Average dialog duration let queryStartDate = ago(14d); let queryEndDate = now(); -customEvents +customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate | where name=="WaterfallStart" | extend DialogId = customDimensions['DialogId'] | extend instanceId = tostring(customDimensions['InstanceId']) -| join kind=leftouter (customEvents | where name=="WaterfallCancel" | extend instanceId = tostring(customDimensions['InstanceId'])) on instanceId -| join kind=leftouter (customEvents | where name=="WaterfallComplete" | extend instanceId = tostring(customDimensions['InstanceId'])) on instanceId -| extend duration = case(not(isnull(timestamp1)), timestamp1 - timestamp, -not(isnull(timestamp2)), timestamp2 - timestamp, 0s) // Abandoned are not counted. Alternate: now()-timestamp) +| join kind=leftouter (customEvents | where name=="WaterfallCancel" | extend instanceId = tostring(customDimensions['InstanceId'])) on instanceId +| join kind=leftouter (customEvents | where name=="WaterfallComplete" | extend instanceId = tostring(customDimensions['InstanceId'])) on instanceId +| extend duration = case(not(isnull(timestamp1)), timestamp1 - timestamp, +not(isnull(timestamp2)), timestamp2 - timestamp, 0s) // Abandoned aren't counted. Alternate: now()-timestamp | extend seconds = round(duration / 1s) | summarize AvgSeconds=avg(seconds) by tostring(DialogId) -| order by AvgSeconds desc nulls last +| order by AvgSeconds desc nulls last | render barchart with (title="Duration in Dialog") ``` -#### Sample query results - -![dialogduration](./media/dialogduration.PNG) +#### Sample average-duration query results +:::image type="content" source="./media/dialogduration.PNG" alt-text="Sample chart of dialog duration."::: ### Average steps in dialog -This example shows each executed dialogs "length" as calculated by average, min, max and standard deviation. This can help analyze dialog quality. For example: -- Dialogs with a lot of steps should be evaluated for simplification opportunities -- Dialogs with a wide gap between min/max/average could mean that users get stalled trying to complete the tasks. You may need to evaluate the possibility of there being shorter paths to complete the tasks, or ways to reduce the dialog complexity. -- Dialogs with a large standard-deviation suggest complex paths or broken experience (abandon/cancel) -- Dialogs with very few steps may be so because they were never completed. Analyzing the completion/abandonment rates may help to make that determination. +This example shows each invoked dialog's "length" as calculated by average, min, max and standard deviation. This can help analyze dialog quality. For example: + +* Dialogs with too many steps should be evaluated for simplification opportunities. +* Dialogs with a wide gap between min/max/average could mean that users get stalled trying to complete the tasks. You may need to evaluate the possibility of there being shorter paths to complete the tasks, or ways to reduce the dialog complexity. +* Dialogs with a large standard-deviation suggest complex paths or broken experience (abandon/cancel). +* Dialogs with few steps may be so because they were never completed. Analyzing the completion/abandonment rates may help to make that determination. ```Kusto // min/max/std/avg steps per dialog @@ -392,21 +395,21 @@ customEvents | extend DialogId = tostring(customDimensions['DialogId']) | extend StepName = tostring(customDimensions['StepName']) | extend InstanceId = tostring(customDimensions['InstanceId']) -| where name == "WaterfallStart" or name == "WaterfallStep" or name == "WaterfallComplete" +| where name == "WaterfallStart" or name == "WaterfallStep" or name == "WaterfallComplete" | order by InstanceId, timestamp asc -| project timestamp, DialogId, name, InstanceId, StepName +| project timestamp, DialogId, name, InstanceId, StepName | summarize cnt=count() by InstanceId, DialogId | summarize avg=avg(cnt), minsteps=min(cnt),maxsteps=max(cnt), std=stdev(cnt) by DialogId | extend avgsteps = round(avg, 1) | extend avgshortbysteps=maxsteps-avgsteps | extend avgshortbypercent=round((1.0 - avgsteps/maxsteps)*100.0, 1) | project DialogId, avgsteps, minsteps, maxsteps, std, avgshortbysteps, avgshortbypercent -| order by std desc nulls last +| order by std desc nulls last ``` -#### Sample query results +#### Sample average-steps query results -| Dialog Id | avg steps | min steps | max steps | std | avg short by steps | avg short by percent | +| Dialog ID | avg steps | min steps | max steps | std | avg short by steps | avg short by percent | | ----------------------- | --------: | :-------: | :-------: | ---: | :----------------: | -------------------: | | FindArticlesDialog | 6.2 | 2 | 7 | 2.04 | 0.8 | 11.4% | | CreateTicket | 4.3 | 2 | 5 | 1.5 | 0.7 | 14% | @@ -416,18 +419,16 @@ customEvents __Interpretation: For example, FindArticlesDialog has a wide spread between min/max and should be investigated and possibly redesigned & optimized. - - ### Channel activity by activity metric -This example measures the amount of activity your bot receives per channel in the given period. It does this by counting any one of the following metrics: incoming messages, users, conversations or dialogs. This can be useful for service health analysis or to measure a channels popularity. +This example measures the amount of activity your bot receives per channel in the given period. It does this by counting any one of the following metrics: incoming messages, users, conversations or dialogs. This can be useful for service health analysis or to measure a channels popularity. ```Kusto // number of metric: messages, users, conversations, dialogs by channel let queryStartDate = ago(14d); let queryEndDate = now(); let groupByInterval = 1d; -customEvents +customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate | extend InstanceId = tostring(customDimensions['InstanceId']) @@ -437,33 +438,29 @@ customEvents | where DialogId != '' and InstanceId != '' and user_Id != '' | extend metric = user_Id // InstanceId or ActivityId or user_Id | summarize Count=count(metric) by ChannelId, bin(timestamp, groupByInterval) -| order by Count desc nulls last +| order by Count desc nulls last | render barchart with (title="Users", kind=stacked) // or Incoming Messages or Conversations or Users ``` > [!TIP] > You may want to consider trying these variations: -> - Run the query without the timestamp bucketing: `bin(timestamp, groupByInterval)` -> - You can also use `dcount` for distinct users vs `count` for all user event activities. This also works for repeat users. -> +> +> * Run the query without the timestamp bucketing: `bin(timestamp, groupByInterval)`. +> * You can also use `dcount` for distinct users and `count` for all user event activities. This also works for repeat users. -#### Sample query results +#### Sample channel-activity-by-activity query results -![ChannelsUsage](./media/ChannelsUsage.PNG) +:::image type="content" source="./media/ChannelsUsage.PNG" alt-text="Sample chart of channel usage."::: _Interpretation: Emulator testing used to be most popular but once we went live, DirectLineSpeech, is the most popular channel._ - -### Total Intents by Popularity -This example applies to LUIS enabled bots. It shows a summary of all [intents](https://aka.ms/botbuilder-luis-concept#recognize-intent) by popularity, and corresponding intent detection certainty score. - -* In practice, the view should separated for each metric. +* In practice, the view should be separated for each metric. * Popular intent paths should be optimized for user experience. * Low average scores indicate poor recognition & possible missing actual user intent. @@ -474,27 +471,27 @@ let queryEndDate = now(); customEvents | where timestamp > queryStartDate | where timestamp < queryEndDate -| where name startswith "LuisResult" +| where name startswith "LuisResult" | extend intentName = tostring(customDimensions['intent']) | extend intentScore = todouble(customDimensions['intentScore']) | summarize ic=count(), ac=avg(intentScore)*100 by intentName | project intentName, ic, ac -| order by ic desc nulls last +| order by ic desc nulls last | render barchart with (kind=unstacked, xcolumn=intentName, ycolumns=ic,ac, title="Intents Popularity") ``` -#### Sample query results +#### Sample intents-by-popularity query results -![IntentPopularity](./media/Telemetry/IntentPopularity.PNG) +:::image type="content" source="./media/Telemetry/IntentPopularity.PNG" alt-text="Sample chart of intent popularity."::: _Interpretation: For example the most popular intent, confirm is detected only with 23% confidence on average._ - > [!TIP] -> Barcharts are one of over a dozen options available with Kusto queries. Some other options include: anomalychart, areachart, columnchart, linechart, scatterchart. for more details see the [render operator](https://aka.ms/kusto-query-render-operator?pivots=Kusto) topic. - +> Barcharts are one of over a dozen options available with Kusto queries. Some other options include: anomalychart, areachart, columnchart, linechart, scatterchart. +> For more information, see the [render operator](/azure/data-explorer/kusto/query/renderoperator) topic. ## Schema of Bot Analytics Instrumentation + The following tables show the most common fields that your bot will log telemetry data into. ### General Envelope @@ -506,19 +503,23 @@ Common log analytics fields in Application Insights instrumentation. | name | Message type | BotMessageSend, BotMessageReceived, LuisResult, WaterfallStep, WaterfallStart, SkillWebSocketProcessRequestLatency, SkillWebSocketOpenCloseLatency, WaterfallComplete, QnaMessage, WaterfallCancel, SkillWebSocketTurnLatency, AuthPromptValidatorAsyncFailure | | customDimensions | SDK Bot Analytics | activityId=\, activityType=message, channelId=emulator, fromId=\, fromName=User, locale=en-us, recipientId=\, recipientName=Bot, text=find a coffee shop | | timestamp | Time of event | 2019-09-05T18:32:45.287082Z | -| instance_Id | Conversation Id | f7b2c416-a680-4b2c-b4cc-79c0f03d8711 | -| operation_Id | Turn Id | 084b2856947e3844a5a18a8476d99aaa | -| user_Id | Unique channel user id | emulator7c259c8e-2f47... | +| instance_Id | Conversation ID | f7b2c416-a680-4b2c-b4cc-79c0f03d8711 | +| operation_Id | Turn ID | 084b2856947e3844a5a18a8476d99aaa | +| user_Id | Unique channel user ID | emulator7c259c8e-2f47... | | client_IP | Client ip address | 127.0.0.1 (may be absent due to privacy block) | | client_City | Client city | Redmond (if detected, may be absent) | +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + ### Custom Dimensions Most of bot specific activity data is stored in _customDimensions_ field. | **Field** | **Description** | **Sample Values** | | ------------- | -------------------- | ------------------------------------------------- | -| activityId | Message id | \: 8da6d750-d00b-11e9-80e0-c14234b3bc2a | +| activityId | Message ID | \: 8da6d750-d00b-11e9-80e0-c14234b3bc2a | | activityType | Type of message | message, conversationUpdate, event, invoke | | channelId | Channel identifier | emulator, directline, msteams, webchat | | fromId | From Identifier | \ | @@ -530,6 +531,8 @@ Most of bot specific activity data is stored in _customDimensions_ field. ### Custom Dimensions: LUIS +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + LUIS instrumentation stores its data in the following Custom Dimensions fields. | **Field** | **Description** | **Sample Values** | @@ -542,23 +545,27 @@ LUIS instrumentation stores its data in the following Custom Dimensions fields. ### Custom Dimensions: QnAMaker +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + QnAMaker instrumentation stores its data in the following Custom Dimensions fields. +> [!TIP] +> To enable logging of personal information like questions and answers, the _log personal information_ parameter should be set to true in the constructor of the _QnA Maker_ class. + | **Field** | **Description** | **Sample Values** | | --------------- | -------------------------- | ------------------------------------------------------------ | | question | QnA detected question | what can you do? | | answer | QnA answer | You have questions, I may have answers. | | articleFound | QnA | true | -| questionId | QnA question Id | 488 | -| knowledgeBaseId | QnA KB Id | 2a4936f3-b2c8-44ff-b21f-67bc413b9727 | -| matchedQuestion | Array of matched questions | ["Can you explain to me what your role is?","Can you tell me a bit about yourself?","Can you tell me about you?","could you help me","hmmm so what can you do?","how can you help me","How can you help me?","How can you help?","so how can i use you in my projects?","Talk to me about your capability","What are you capable of?",… | - - +| questionId | QnA question ID | 488 | +| knowledgeBaseId | QnA KB ID | 2a4936f3-b2c8-44ff-b21f-67bc413b9727 | +| matchedQuestion | Array of matched questions | ["Can you explain to me what your role is?","Can you tell me a bit about yourself?","Can you tell me about you?","could you help me","hmmm so what can you do?","how can you help me","How can you help me?","How can you help?","so how can i use you in my projects?","Talk to me about your capability","What are you capable of?", ...] | ## See Also -* For a tutorial on writing log queries, see [Get started with log queries in Azure Monitor](https://aka.ms/azure-monitor-log-queries-get-started) -* [Visualizing data from Azure Monitor](https://aka.ms/azure-monitor-visualize-data) -* Learn how to [Add telemetry to your bot](https://aka.ms/add-bot-telemetry) -* Learn more about [Azure Monitor log queries](https://aka.ms/azure-monitor-log-queries) -* [Create and share dashboards of Log Analytics data](https://aka.ms/log-analytics-create-share-dashboards) +* For a tutorial on writing log queries, see [Get started with log queries in Azure Monitor](/azure/azure-monitor/log-query/get-started-queries) +* [Visualizing data from Azure Monitor](/azure/azure-monitor/visualizations) +* Learn how to [Add telemetry to your bot](bot-builder-telemetry.md) +* Learn more about [Azure Monitor log queries](/azure/data-explorer/using-diagnostic-logs) +* Complete listing of Bot Framework [Application Insights Events](/azure/bot-service/bot-builder-telemetry-reference) +* [Create and share dashboards of Log Analytics data](/azure/azure-monitor/learn/tutorial-logs-dashboards) diff --git a/articles/v4sdk/bot-builder-telemetry-qnamaker.md b/articles/v4sdk/bot-builder-telemetry-qnamaker.md index 3599595c5..bf2fa6c09 100644 --- a/articles/v4sdk/bot-builder-telemetry-qnamaker.md +++ b/articles/v4sdk/bot-builder-telemetry-qnamaker.md @@ -1,70 +1,66 @@ --- -title: Add telemetry to your QnA bot - Bot Service -description: Learn how to integrate the new telemetry features into your QnA Maker enabled bot. +title: Add telemetry features to your QnA Maker bot +description: Learn how to integrate telemetry features into your QnA Maker enabled bot and send event data to telemetry services like Application Insights. keywords: telemetry, appinsights, Application Insights, monitor bot, QnA Maker -author: WashingtonKayaker -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 07/31/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - abs-meta-21q1 + - evergreen --- -# Add telemetry to your QnAMaker bot +# Add telemetry to your QnA Maker bot -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] -Telemetry logging was added to version 4.2 of the Bot Framework SDK. This enables bot applications to send event data to telemetry services such as [Application Insights](https://aka.ms/appinsights-overview). Telemetry offers insights into your bot by showing which features are used the most, detects unwanted behavior and offers visibility into availability, performance, and usage. +Telemetry logging lets bot applications send event data to telemetry services such as [Application Insights](/azure/azure-monitor/app/app-insights-overview/). Telemetry offers insights into your bot by showing which features are used the most, detects unwanted behavior and offers visibility into availability, performance, and usage. -Two new components were added to the Bot Framework SDK that enable telemetry logging in QnA Maker enabled bots: `TelemetryLoggerMiddleware` and the `QnAMaker` class. `TelemetryLoggerMiddleware` is a middleware component that logs every time messages are received, sent, updated, or deleted, and the 'QnAMaker' class provides custom logging that extends telemetry capabilities. +The `TelemetryLoggerMiddleware` and `QnAMaker` classes in the Bot Framework SDK enable telemetry logging in QnA Maker enabled bots. `TelemetryLoggerMiddleware` is a middleware component that logs telemetry every time messages are received, sent, updated, or deleted, and the `QnAMaker` class provides custom logging that extends telemetry capabilities. -In this article you will learn about: - -* The code required to wire up telemetry in your bot - -* The code required to enable the out-of-the-box QnA logging and reports that use the standard event properties. - -* Modifying or extending the SDK's default event properties to enable a wide range of reporting needs. +In this article you'll learn about: +- The code required to wire up telemetry in your bot +- The code required to enable the out-of-the-box QnA Maker logging and reports that use the standard event properties. +- Modifying or extending the SDK's default event properties to enable a wide range of reporting needs. ## Prerequisites -* The [QnA Maker sample code](https://aka.ms/cs-qna) - -* A subscription to [Microsoft Azure](https://portal.azure.com/) - -* An [Application Insights key](../bot-service-resources-app-insights-keys.md) - -* Familiarity with [QnA Maker](https://qnamaker.ai/) is helpful. - -* A [QnA Maker](https://aka.ms/create-qna-maker) account. - -* A published QnA Maker knowledge base. If you do not have one, follow the steps in [Create and answer from KB](https://aka.ms/create-publish-query-in-portal) tutorial to create a QnA Maker knowledge base with questions and answers. +- The [QnA Maker sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/archive/csharp_dotnetcore/11.qnamaker) +- A subscription to [Microsoft Azure](https://portal.azure.com/) +- An [Application Insights key](../bot-service-resources-app-insights-keys.md) +- Familiarity with [QnA Maker](https://qnamaker.ai/) is helpful. +- A [QnA Maker](/azure/ai-services/qnamaker/how-to/set-up-qnamaker-service-azure) account. +- An _existing_ and published QnA Maker knowledge base. > [!NOTE] -> This article will build on the [QnA Maker sample code](https://aka.ms/cs-qna) by stepping you through the steps required to incorporate telemetry. - -## Wiring up telemetry in your QnA Maker bot +> This article builds on the [QnA Maker sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/archive/csharp_dotnetcore/11.qnamaker) by stepping you through the steps required to incorporate telemetry. -We will start with the [QnA Maker sample app](https://aka.ms/cs-qna) and add the code required to integrate telemetry into a bot using the QnA service. This will enable Application Insights to begin tracking requests. +## Add telemetry code to your QnA Maker bot -1. Open the [QnA Maker sample app](https://aka.ms/cs-qna) in Visual Studio +We'll start with the [QnA Maker sample app](https://github.com/microsoft/BotBuilder-Samples/tree/master/archive/csharp_dotnetcore/11.qnamaker) and add the code required to integrate telemetry into a bot that uses the QnA Maker service. This will enable Application Insights to track requests. -2. Add the `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core ` NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs): +1. Open the [QnA Maker sample app](https://github.com/microsoft/BotBuilder-Samples/tree/master/archive/csharp_dotnetcore/11.qnamaker) in Visual Studio. +1. Add the `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core` NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](/nuget/tools/package-manager-ui): +1. Include the following statements in `Startup.cs`: -3. Include the following statements in `Startup.cs`: ```csharp using Microsoft.ApplicationInsights.Extensibility; using Microsoft.Bot.Builder.ApplicationInsights; using Microsoft.Bot.Builder.Integration.ApplicationInsights.Core; ``` - > [!NOTE] - > If you're following along by updating the QnA Maker sample code you will notice that the using statement for `Microsoft.Bot.Builder.Integration.AspNet.Core` already exists in the QnA Maker sample. + > [!NOTE] + > If you're following along by updating the QnA Maker sample code you'll notice that the using statement for `Microsoft.Bot.Builder.Integration.AspNet.Core` already exists in the QnA Maker sample. + +1. Add the following code to the `ConfigureServices()` method in `Startup.cs`. This makes telemetry services available to your bot via [dependency injection (DI)](/aspnet/core/fundamentals/dependency-injection): -4. Add the following code to the `ConfigureServices()` method in `Startup.cs`. This makes telemetry services available to your bot via [dependency injection (DI)](https://aka.ms/asp.net-core-dependency-interjection): ```csharp // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) @@ -93,11 +89,12 @@ We will start with the [QnA Maker sample app](https://aka.ms/cs-qna) and add the ... } ``` - - > [!NOTE] - > If you are following along by updating the QnA Maker sample code you will notice that `services.AddSingleton();` already exists. -5. Instruct the adapter to use the middleware code that was added to the `ConfigureServices()` method. Open `AdapterWithErrorHandler.cs` and add `IMiddleware middleware` to the constructors parameter list. Add the `Use(middleware);` statement as the last line in the contructor: + > [!NOTE] + > If you're following along by updating the QnA Maker sample code, you'll notice that `services.AddSingleton();` already exists. + +1. Instruct the adapter to use the middleware code that was added to the `ConfigureServices()` method. Open `AdapterWithErrorHandler.cs` and add `IMiddleware middleware` to the constructors parameter list. Add the `Use(middleware);` statement as the last line in the constructor: + ```csharp public AdapterWithErrorHandler(ICredentialProvider credentialProvider, ILogger logger, IMiddleware middleware, ConversationState conversationState = null) : base(credentialProvider) @@ -108,7 +105,7 @@ We will start with the [QnA Maker sample app](https://aka.ms/cs-qna) and add the } ``` -6. Add the Application Insights instrumentation key in your `appsettings.json` file. The `appsettings.json` file contains metadata about external services the Bot uses while running. For example, CosmosDB, Application Insights and the QnA Maker service connection and metadata is stored there. The addition to your `appsettings.json` file must be in this format: +1. Add the Application Insights instrumentation key in your `appsettings.json` file. The `appsettings.json` file contains metadata about external services the bot uses while running, such as connection and metadata for Cosmos DB, Application Insights, and QnA Maker. The addition to your `appsettings.json` file must be in this format: ```json { @@ -122,27 +119,24 @@ We will start with the [QnA Maker sample app](https://aka.ms/cs-qna) and add the } } ``` - > [!Note] - > - > * Details on getting the _Application Insights instrumentation key_ can be found in the article [Application Insights keys](../bot-service-resources-app-insights-keys.md). - > - > * You should already have a [QnA maker account](https://aka.ms/create-qna-maker), if needed you can find information on getting the QnA Knowledgebase Id, Endpoint Key and HostName values [here](https://aka.ms/bot-framework-emulator-qna-keys). - > + > [!NOTE] + > + > - Details on getting the _Application Insights instrumentation key_ can be found in the article [Application Insights keys](../bot-service-resources-app-insights-keys.md). + > - You should already have a [QnA Maker account](/azure/ai-services/qnamaker/how-to/set-up-qnamaker-service-azure). For information on getting the QnA Maker knowledge base ID, endpoint key and host values, see the [Publish to get GenerateAnswer endpoint](/azure/ai-services/qnamaker/how-to/metadata-generateanswer-usage#publish-to-get-generateanswer-endpoint) section of QnA Maker's **Get an answer with the GenerateAnswer API** article. -At this point the preliminary work to enable telemetry using Application Insights is done. You can run your bot locally using the bot emulator and then go into Application Insights to see what is being logged such as response time, overall app health, and general running information. - -> [!TIP] -> For information on Enabling / disabling activity event and personal information logging see [Add telemetry to your bot](bot-builder-telemetry.md#enabling-or-disabling-activity-logging) +At this point, the preliminary work to enable telemetry using Application Insights is done. You can run your bot locally using the Bot Framework Emulator and then go into Application Insights to see what is being logged such as response time, overall app health, and general running information. -Next we will see what needs to be included to add telemetry functionality to the QnA Maker service. +> [!TIP] +> For information about personal information, see [Enable or disable activity event and personal information logging](bot-builder-telemetry.md#enable-or-disable-activity-event-and-personal-information-logging). +Next we'll see what needs to be included to add telemetry functionality to the QnA Maker service. -## Enabling telemetry to capture usage data from the QnA Maker service +## Enable telemetry to capture usage data from the QnA Maker service -The QnA Maker service has built-in telemetry logging available so there is very little you need to do to start getting telemetry data from QnA Maker. First we will see how to incorporate telemetry into the QnA Maker code to enable the built-in telemetry logging, then we will learn how to replace or add additional properties to the existing event data to satisfy a wide range of reporting needs. +The QnA Maker service has built-in telemetry logging available, so there's little you need to do to start getting telemetry data from QnA Maker. First we'll see how to incorporate telemetry into the QnA Maker code to enable the built-in telemetry logging, then we'll learn how to replace or add properties to the existing event data to satisfy a wide range of reporting needs. -### Enabling default QnA logging +### Enable default QnA Maker logging 1. Create a private readonly field of type `IBotTelemetryClient` in your `QnABot` class in `QnABot.cs`: @@ -154,7 +148,7 @@ The QnA Maker service has built-in telemetry logging available so there is very } ``` -2. Add an `IBotTelemetryClient` parameter to your `QnABot` class constructor in `QnABot.cs` and assign its value to the private field created in the previous step: +1. Add an `IBotTelemetryClient` parameter to your `QnABot` class constructor in `QnABot.cs` and assign its value to the private field created in the previous step: ```cs public QnABot(IConfiguration configuration, ILogger logger, IHttpClientFactory httpClientFactory, IBotTelemetryClient telemetryClient) @@ -164,7 +158,7 @@ The QnA Maker service has built-in telemetry logging available so there is very } ``` -3. The _`telemetryClient`_ parameter is required when instantiating the new QnAMaker object in `QnABot.cs`: +1. The _`telemetryClient`_ parameter is required when instantiating the new QnAMaker object in `QnABot.cs`: ```cs var qnaMaker = new QnAMaker(new QnAMakerEndpoint @@ -178,54 +172,47 @@ The QnA Maker service has built-in telemetry logging available so there is very _telemetryClient); ``` - > [!TIP] - > Make sure that the property names that you use in the `_configuration` entries match the property names you used in the AppSettings.json file and the values for those properties are obtained by selecting the _View Code_ button in https://www.qnamaker.ai/Home/MyServices: - - - ![AppSettings](media/AppSettings.json-QnAMaker.png) + > [!TIP] + > Make sure that the property names that you use in the `_configuration` entries match the property names you used in the AppSettings.json file and the values for those properties are obtained by selecting the _View Code_ button on the [My knowledge bases](https://www.qnamaker.ai/Home/MyServices) page in the QnA Maker portal: -#### Viewing Telemetry data logged from the QnA Maker default entries -You can view the results of your QnA Maker bot usage in Application Insights after running your bot in the bot emulator by taking the following steps : + :::image type="content" source="media/AppSettings.json-QnAMaker.png" alt-text="Illustration of where the app settings are found in the LUIS portal."::: -1. Go to the [Azure portal](https://portal.azure.com/) +#### View telemetry data logged from the QnA Maker default entries -2. Navigate to your Application Insights by clicking on __Monitor > Applications__. +You can view the results of your QnA Maker bot usage in Application Insights after running your bot in the Bot Framework Emulator by taking the following steps: -3. Once in your Application Insights, click on _Logs (Analytics)_ on the navigation bar as shown below: +1. In the [Azure portal](https://portal.azure.com/), go to the Application Insights resource for your bot. +1. Under **Monitoring**, select **Logs**. +1. Enter the following Kusto query, then select **Run**. - ![Log Analytics](media/AppInsights-LogView-QnaBot.png) - -4. Enter the following Kusto query and then select _Run_ - - ```SQL + ```kusto customEvents | where name == 'QnaMessage' | extend answer = tostring(customDimensions.answer) | summarize count() by answer ``` -5. Leave this page open in your browser, we will come back to it after adding a new custom property. -> [!TIP] -> If you are new to the Kusto query language that is used to write log queries in Azure Monitor, but are familiar with SQL query language, you may find the [SQL to Azure Monitor log query cheat sheet](https://aka.ms/azureMonitor-SQL-cheatsheet) useful. +1. Leave this page open in your browser; we'll come back to it after adding a new custom property. -### Modifying or extending the default event properties -If you need properties that are not defined in the `QnAMaker` class there are two ways of handling this, both require creating your own class derived from the `QnAMaker` class. The first is explained in the section below titled [Adding properties](#adding-properties) in which you add properties to the existing `QnAMessage` event. The second method allows you to create new events to which you can add properties as described in [Adding new events with custom properties](#adding-new-events-with-custom-properties). - -> [!Note] -> The `QnAMessage` event is part of the Bot Framework SDK and provides all of the out-of-the-box event properties that are logged to Application Insights. +> [!TIP] +> If you're new to the Kusto query language that's used to write log queries in Azure Monitor, but are familiar with SQL query language, you may find the [SQL to Azure Monitor log query cheat sheet](/azure/azure-monitor/log-query/sql-cheatsheet) useful. +### Modify or extend default event properties +If you need properties that aren't defined in the `QnAMaker` class there are two ways of handling this, both require creating your own class derived from the `QnAMaker` class. The first is explained in the section below titled [Adding properties](#add-properties) in which you add properties to the existing `QnAMessage` event. The second method allows you to create new events to which you can add properties as described in [Adding new events with custom properties](#add-new-events-with-custom-properties). -#### Adding properties +> [!NOTE] +> The `QnAMessage` event is part of the Bot Framework SDK and provides all of the out-of-the-box event properties that are logged to Application Insights. -The following demonstrates how you can derive from the `QnAMaker` class. The example shows adding the property "MyImportantProperty" to the `QnAMessage` event. The `QnAMessage` event is logged every time a QnA [GetAnswers](https://aka.ms/namespace-QnAMaker-GetAnswersAsync) call is performed. +#### Add properties -After learning how to add custom properties we will learn how to create a new custom event and associate properties with it, then we will run the bot locally using the Bot Framework Emulator and see what is being logged in Application Insights using the Kusto query language. +The following demonstrates how you can derive from the `QnAMaker` class. The example shows adding the property "MyImportantProperty" to the `QnAMessage` event. The `QnAMessage` event is logged every time a QnA [GetAnswers](/dotnet/api/microsoft.bot.builder.ai.qna.qnamaker.getanswersasync?view=botbuilder-dotnet-stable&preserve-view=true) call is performed. -1. Create a new class named `MyQnAMaker` in the `Microsoft.BotBuilderSamples` namespace that inherits from the `QnAMaker` class and save it as `MyQnAMaker.cs`. In order to inherit from the `QnAMaker` class you will need to add the `Microsoft.Bot.Builder.AI.QnA` using statement. Your code should appear as follows: +After learning how to add custom properties we'll learn how to create a new custom event and associate properties with it, then we'll run the bot locally using the Bot Framework Emulator and see what is being logged in Application Insights using the Kusto query language. +1. Create a new class named `MyQnAMaker` in the `Microsoft.BotBuilderSamples` namespace that inherits from the `QnAMaker` class and save it as `MyQnAMaker.cs`. To inherit from the `QnAMaker` class, you'll need to add the `Microsoft.Bot.Builder.AI.QnA` using statement. Your code should appear as follows: - ```cs + ```csharp using Microsoft.Bot.Builder.AI.QnA; namespace Microsoft.BotBuilderSamples @@ -236,10 +223,10 @@ After learning how to add custom properties we will learn how to create a new cu } } ``` -2. Add a class constructor to `MyQnAMaker`. Note that you will need two additional using statements for the constructors parameters `System.Net.Http` and `Microsoft.Bot.Builder`: - ```cs - ... +1. Add a class constructor to `MyQnAMaker`. You'll need two more using statements for the constructor parameters for `System.Net.Http` and `Microsoft.Bot.Builder`: + + ```csharp using Microsoft.Bot.Builder.AI.QnA; using System.Net.Http; using Microsoft.Bot.Builder; @@ -258,19 +245,20 @@ After learning how to add custom properties we will learn how to create a new cu { } - } - } + } + } ``` -3. Add the new property to the QnAMessage event after the constructor and include the statements `System.Collections.Generic`, `System.Threading`, and `System.Threading.Tasks`: - ```cs +1. Add the new property to the QnAMessage event after the constructor and include the statements `System.Collections.Generic`, `System.Threading`, and `System.Threading.Tasks`: + + ```csharp using Microsoft.Bot.Builder.AI.QnA; using System.Net.Http; using Microsoft.Bot.Builder; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - + namespace Microsoft.BotBuilderSamples { public class MyQnAMaker : QnAMaker @@ -303,13 +291,13 @@ After learning how to add custom properties we will learn how to create a new cu ); } - } - } + } + } ``` -4. Modify your bot to use the new class, instead of creating a `QnAMaker` object you will create a `MyQnAMaker` object in `QnABot.cs`: +1. Modify your bot to use the new class, instead of creating a `QnAMaker` object you'll create a `MyQnAMaker` object in `QnABot.cs`: - ```cs + ```csharp var qnaMaker = new MyQnAMaker(new QnAMakerEndpoint { KnowledgeBaseId = _configuration["QnAKnowledgebaseId"], @@ -321,31 +309,33 @@ After learning how to add custom properties we will learn how to create a new cu _telemetryClient); ``` -##### Viewing telemetry data logged from the new property _MyImportantProperty_ -After running your bot in the emulator you can view the results in Application Insights by doing the following: +##### View telemetry data logged from the new property _MyImportantProperty_ -1. Switch back to your browser that has the _Logs (Analytics)_ view active. +After running your bot in the Emulator, you can view the results in Application Insights by doing the following: -2. Enter the following Kusto query and then select _Run_. This will give a count of the number of times the new property was executed: +1. Switch back to your browser that has the _Logs (Analytics)_ view active. +1. Enter the following Kusto query and then select **Run**. This will give a count of the number of times the new property was executed: - ```SQL + ```kusto customEvents | where name == 'QnaMessage' | extend MyImportantProperty = tostring(customDimensions.MyImportantProperty) | summarize count() by MyImportantProperty ``` -3. To show details instead of the count remove the last line and re-run the query: +1. To show details instead of the count, remove the last line and rerun the query: - ```SQL + ```kusto customEvents | where name == 'QnaMessage' | extend MyImportantProperty = tostring(customDimensions.MyImportantProperty) ``` -### Adding new events with custom properties -If you need to log data to a different event than `QnaMessage`, you can create your own custom event with its own properties. To do this, we will add code to the end of the `MyQnAMaker` class as follows: -```CS +### Add new events with custom properties + +If you need to log data to a different event than `QnaMessage`, you can create your own custom event with its own properties. To do this, we'll add code to the end of the `MyQnAMaker` class as follows: + +```csharp public class MyQnAMaker : QnAMaker { ... @@ -363,26 +353,20 @@ public class MyQnAMaker : QnAMaker "MySecondEvent", secondEventProperties); -} -``` -## The Application Insights dashboard - -Anytime you create an Application Insights resource in Azure, a new dashboard will automatically be created and associated with it. You can see that dashboard by selecting the button at the top of your Application Insights blade, labeled **Application Dashboard**. - -![Application Dashboard Link](media/Application-Dashboard-Link.png) +} +``` +## The Application Insights dashboard -Alternatively, to view the data, go to the Azure portal. Click **Dashboard** on the left, then select the dashboard you want from the drop-down. +Anytime you create an Application Insights resource in Azure, Azure creates a new dashboard associated with your resource. To display the dashboard from the Application Insights blade, select **Application Dashboard**. -There you'll see some default information about your bot performance and any additional queries that you've pinned to your dashboard. +Alternatively, to view the data, go to the Azure portal, expand the portal menu, then select **Dashboard**. Then, select the dashboard you want from the drop-down menu. +The dashboard displays some default information about your bot performance and any other queries that you've pinned to your dashboard. ## Additional Information -* [Add telemetry to your bot](bot-builder-telemetry.md) - -* [What is Application Insights?](https://aka.ms/appinsights-overview) - -* [Using Search in Application Insights](https://aka.ms/search-in-application-insights) - -* [Create custom KPI dashboards using Azure Application Insights](https://aka.ms/custom-kpi-dashboards-application-insights) +- [Add telemetry to your bot](bot-builder-telemetry.md) +- [What is Application Insights?](/azure/azure-monitor/app/app-insights-overview/) +- [Using Search in Application Insights](/azure/azure-monitor/app/diagnostic-search/) +- [Create custom KPI dashboards using Azure Application Insights](/azure/azure-monitor/learn/tutorial-app-dashboards/) diff --git a/articles/v4sdk/bot-builder-telemetry-reference.md b/articles/v4sdk/bot-builder-telemetry-reference.md deleted file mode 100644 index e8b8496e8..000000000 --- a/articles/v4sdk/bot-builder-telemetry-reference.md +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: Events generated by the Bot Framework Service telemetry - Bot Service -description: Learn what events are triggered with the new telemetry features. -keywords: telemetry, appinsights, monitor bot -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Events generated by the Bot Framework Service telemetry - -## Channel service events - -In addition to `WaterfallDialog`, which was discussed in the [telemetry topic](bot-builder-telemetry.md) and which generates events from your bot code, the Bot Framework Channel service also logs events. This helps you diagnose issues with Channels or overall bot failures. - -### CustomEvent: "Activity" -**Logged From:** Channel Service -Logged by the Channel Service when a message received. - -### Exception: "Bot Errors" -**Logged From:** Channel Service -Logged by the channel when a call to the Bot returns a non-2XX Http Response. - -## CustomEvent: "WaterfallStart" - -When a WaterfallDialog begins, a `WaterfallStart` event is logged. - -- `user_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `session_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityType` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.channelId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.DialogId` (This is the dialogId (string) passed into your Waterfall. You can consider this the "waterfall type") -- `customDimensions.InstanceID` (unique per instance of the dialog) - -## CustomEvent: "WaterfallStep" - -Logs individual steps from a Waterfall Dialog. - -- `user_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `session_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityType` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.channelId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.DialogId` (This is the dialogId (string) passed into your Waterfall. You can consider this the "waterfall type") -- `customDimensions.StepName` (either method name or `StepXofY` if lambda) -- `customDimensions.InstanceID` (unique per instance of the dialog) - -## CustomEvent: "WaterfallDialogComplete" - -Logs when a Waterfall Dialog completes. - -- `user_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `session_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityType` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.channelId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.DialogId` (This is the dialogId (string) passed into your Waterfall. You can consider this the "waterfall type") -- `customDimensions.InstanceID` (unique per instance of the dialog) - -## CustomEvent: "WaterfallDialogCancel" - -Logs when a Waterfall Dialog is canceled. - -- `user_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `session_id` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.activityType` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.channelId` ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- `customDimensions.DialogId` (This is the dialogId (string) passed into your Waterfall. You can consider this the "waterfall type") -- `customDimensions.StepName` (either method name or `StepXofY` if lambda) -- `customDimensions.InstanceID` (unique per instance of the dialog) - -## CustomEvent: BotMessageReceived -Logged when bot receives new message from a user. - -When not overridden, this event is logged from `Microsoft.Bot.Builder.TelemetryLoggerMiddleware` using the `Microsoft.Bot.Builder.IBotTelemetry.TrackEvent()` method. - -- Session Identifier - - When using Application Insights, this is logged from the `TelemetryBotIdInitializer` as the **session** identifier (*Temeletry.Context.Session.Id*) used within Application Insights. - - Corresponds to the [Conversation ID](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#conversation) as defined by Bot Framework protocol.. - - The property name logged is `session_id`. - -- User Identifier - - When using Application Insights, this is logged from the `TelemetryBotIdInitializer` as the **user** identifier (*Telemetry.Context.User.Id*) used within Application Insights. - - The value of this property is a combination of the [Channel Identifier](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#channel-id) and the [User ID](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) (concatenated together) properties as defined by the Bot Framework protocol. - - The property name logged is `user_id`. - -- ActivityID - - When using Application Insights, this is logged from the `TelemetryBotIdInitializer` as a Property to the event. - - Corresponds to the [Activity ID](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#Id) as defined by Bot Framework protocol.. - - The property name is `activityId`. - -- Channel Identifier - - When using Application Insights, this is logged from the `TelemetryBotIdInitializer` as a Property to the event. - - Corresponds to the [Channel Identifier](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#id) of the Bot Framework protocol. - - The property name logged is `channelId`. - -- ActivityType - - When using Application Insights, this is logged from the `TelemetryBotIdInitializer` as a Property to the event. - - Corresponds to the [Activity Type](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#type) of the Bot Framework protocol. - - The property name logged is `activityType`. - -- Text - - **Optionally** logged when the `logPersonalInformation` property is set to `true`. - - Corresponds to the [Activity Text](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#text) field of the Bot Framework protocol. - - The property name logged is `text`. - -- Speak - - - **Optionally** logged when the `logPersonalInformation` property is set to `true`. - - Corresponds to the [Activity Speak](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#speak) field of the Bot Framework protocol. - - The property name logged is `speak`. - - - - -- FromId - - Corresponds to the [From Identifier](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromId`. - -- FromName - - **Optionally** logged when the `logPersonalInformation` property is set to `true`. - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -- RecipientId - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -- RecipientName - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -- ConversationId - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -- ConversationName - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -- Locale - - Corresponds to the [From Name](https://github.com/Microsoft/BotBuilder/blob/master/specs/botframework-activity/botframework-activity.md#from) field of the Bot Framework protocol. - - The property name logged is `fromName`. - -## CustomEvent: BotMessageSend -**Logged From:** TelemetryLoggerMiddleware - -Logged when bot sends a message. - -- UserID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- SessionID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Channel ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityType ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ReplyToID -- RecipientId -- ConversationName -- Locale -- RecipientName (Optional for PII) -- Text (Optional for PII) -- Speak (Optional for PII) - - -## CustomEvent: BotMessageUpdate -**Logged From:** TelemetryLoggerMiddleware -Logged when a message is updated by the bot (rare case) -- UserID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- SessionID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Channel ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityType ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- RecipientId -- ConversationId -- ConversationName -- Locale -- Text (Optional for PII) - - -## CustomEvent: BotMessageDelete -**Logged From:** TelemetryLoggerMiddleware -Logged when a message is deleted by the bot (rare case) -- UserID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- SessionID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Channel ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityType ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- RecipientId -- ConversationId -- ConversationName - -## CustomEvent: LuisEvent -**Logged From:** LuisRecognizer - -Logs results from LUIS service. - -- UserID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- SessionID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Channel ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityType ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ApplicationId -- Intent -- IntentScore -- Intent2 -- IntentScore2 -- FromId -- SentimentLabel -- SentimentScore -- Entities (as json) -- Question (Optional for PII) - -## CustomEvent: QnAMessage -**Logged From:** QnAMaker - -Logs results from QnA Maker service. - -- UserID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- SessionID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityID ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Channel ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- ActivityType ([From Telemetry Initializer](https://aka.ms/telemetry-initializer)) -- Username (Optional for PII) -- Question (Optional for PII) -- MatchedQuestion -- QuestionId -- Answer -- Score -- ArticleFound - diff --git a/articles/v4sdk/bot-builder-telemetry.md b/articles/v4sdk/bot-builder-telemetry.md index 0b21862fe..dc2b04036 100644 --- a/articles/v4sdk/bot-builder-telemetry.md +++ b/articles/v4sdk/bot-builder-telemetry.md @@ -1,61 +1,79 @@ --- -title: Add telemetry to your bot - Bot Service -description: Learn how to integrate your bot with the new telemetry features. +title: Add telemetry to your bot +description: Learn how to view information on bot availability, performance, usage, and behavior. See how to turn on telemetry tracking for Application Insights. keywords: telemetry, appinsights, monitor bot -author: WashingtonKayaker -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 07/17/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Add telemetry to your bot -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] +Telemetry logging enables bot applications to send event data to telemetry services such as [Application Insights](/azure/azure-monitor/app/app-insights-overview/). Telemetry offers insights into your bot by showing which features are used the most, detects unwanted behavior, and offers visibility into availability, performance, and usage. -Telemetry logging was added to version 4.2 of the Bot Framework SDK. This enables bot applications to send event data to telemetry services such as [Application Insights](https://aka.ms/appinsights-overview). Telemetry offers insights into your bot by showing which features are used the most, detects unwanted behavior and offers visibility into availability, performance, and usage. +This article describes how to implement telemetry in your bot using Application Insights. This article covers: -***Note: In version 4.6, the standard method for implementing telemetry into a bot has been updated in order to ensure telemetry is logged correctly when using a custom adapter. This article has been updated to show the updated method. The changes are backwards compatible and bots using the previous method will continue to log telemetry correctly.*** +* The code required to wire up telemetry in your bot and connect to Application Insights. +* How to enable telemetry in your bot's [Dialogs](bot-builder-concept-dialog.md). +* How to enable telemetry to capture usage data from other services, like Azure AI services. +* How to visualize your telemetry data in Application Insights. +> [!IMPORTANT] +> For a regional bot that might collect personally identifiable information (PII) in telemetry, your Application Insights resource and your Azure Bot resource should be in the same region with the bot. If the resources are in different regions, the PII might leave the geographic region of the bot. -In this article you will learn how to implement telemetry into your bot using Application Insights: - -* The code required to wire up telemetry in your bot and connect to Application Insights - -* Enabling telemetry in your bots [Dialogs](bot-builder-concept-dialog.md) - -* Enabling telemetry to capture usage data from other services like [LUIS](bot-builder-howto-v4-luis.md) and [QnA Maker](bot-builder-howto-qna.md). +## Prerequisites -* Visualizing your telemetry data in Application Insights +# [C#](#tab/csharp) -## Prerequisites +* The [CoreBot sample code](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot) +* The [Application Insights sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/21.corebot-app-insights) +* A subscription to [Microsoft Azure](https://portal.azure.com/) +* An [Application Insights key](../bot-service-resources-app-insights-keys.md) +* Familiarity with [Application Insights](/azure/azure-monitor/app/app-insights-overview/) +* [git](https://git-scm.com/) -* The [CoreBot sample code](https://aka.ms/cs-core-sample) +> [!NOTE] +> The [Application Insights sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/21.corebot-app-insights) was built on top of the [CoreBot sample code](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot). This article will step you through modifying the CoreBot sample code to incorporate telemetry. If you're following along in Visual Studio, you'll have the Application Insights sample code by the time you're finished. -* The [Application Insights sample code](https://aka.ms/csharp-corebot-app-insights-sample) +# [JavaScript](#tab/javascript) +* The [CoreBot sample code](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot) +* The [Application Insights sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/21.corebot-app-insights) * A subscription to [Microsoft Azure](https://portal.azure.com/) - * An [Application Insights key](../bot-service-resources-app-insights-keys.md) - -* Familiarity with [Application Insights](https://aka.ms/appinsights-overview) +* Familiarity with [Application Insights](/azure/azure-monitor/app/app-insights-overview/) +* [Visual Studio Code](https://www.visualstudio.com/downloads) +* [Node.js](https://nodejs.org/) version 10.14 or later. Use command `node --version` to determine the version of node you have. +* [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) > [!NOTE] -> The [Application Insights sample code](https://aka.ms/csharp-corebot-app-insights-sample) was built on top of the [CoreBot sample code](https://aka.ms/cs-core-sample). This article will step you through modifying the CoreBot sample code to incorporate telemetry. If you are following along in Visual Studio you will have the Application Insights sample code by the time we are finished. +> The [Application Insights sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/21.corebot-app-insights) was built on top of the [CoreBot sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot). This article will step you through modifying the CoreBot sample code to incorporate telemetry. If you're following along in Visual Studio Code, you'll have the Application Insights sample code by the time you're finished. + +--- + +## Enable telemetry in your bot + +# [C#](#tab/csharp) -## Wiring up telemetry in your bot +This article starts from the [CoreBot sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot) and adds the code required to integrate telemetry into any bot. This will enable Application Insights to begin tracking requests. -We will start with the [CoreBot sample app](https://aka.ms/cs-core-sample) and add the code required to integrate telemetry into any bot. This will enable Application Insights to begin tracking requests. +> [!IMPORTANT] +> If you haven't setup your [Application Insights](/azure/azure-monitor/app/app-insights-overview) account and created your [Application Insights key](../bot-service-resources-app-insights-keys.md), do that before proceeding. -1. Open the [CoreBot sample app](https://aka.ms/cs-core-sample) in Visual Studio +1. Open the [CoreBot sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/13.core-bot) in Visual Studio. -2. Add the `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core ` NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](https://aka.ms/install-manage-packages-vs): +1. Add the `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core` NuGet package. For more information on using NuGet, see [Install and manage packages in Visual Studio](/nuget/consume-packages/install-use-packages-visual-studio): +1. Include the following statements in `Startup.cs`: -3. Include the following statements in `Startup.cs`: ```csharp using Microsoft.ApplicationInsights.Extensibility; using Microsoft.Bot.Builder.ApplicationInsights; @@ -63,9 +81,11 @@ We will start with the [CoreBot sample app](https://aka.ms/cs-core-sample) and a using Microsoft.Bot.Builder.Integration.AspNet.Core; ``` - Note: If you're following along by updating the CoreBot sample code you will notice that the using statement for `Microsoft.Bot.Builder.Integration.AspNet.Core` already exists in the CoreBot sample. + > [!TIP] + > If you're following along by updating the CoreBot sample code, you'll notice that the using statement for `Microsoft.Bot.Builder.Integration.AspNet.Core` already exists in the CoreBot sample. + +1. Include the following code in the `ConfigureServices()` method in `Startup.cs`. This will make telemetry services available to your bot via [dependency injection (DI)](/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2&preserve-view=true): -4. Include the following code in the `ConfigureServices()` method in `Startup.cs`. This will make telemetry services available to your bot via [dependency injection (DI)](https://aka.ms/asp.net-core-dependency-interjection): ```csharp // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) @@ -95,9 +115,11 @@ We will start with the [CoreBot sample app](https://aka.ms/cs-core-sample) and a } ``` - Note: If your following along by updating the CoreBot sample code you will notice that `services.AddSingleton();` already exists + > [!TIP] + > If you're following along by updating the CoreBot sample code, you'll notice that `services.AddSingleton();` already exists. + +1. Instruct the adapter to use the middleware code that was added to the `ConfigureServices()` method. You do this in `AdapterWithErrorHandler.cs` with the parameter TelemetryInitializerMiddleware telemetryInitializerMiddleware in the constructor's parameter list, and the `Use(telemetryInitializerMiddleware);` statement in the constructor as shown here: -5. Instruct the adapter to use the middleware code that was added to the `ConfigureServices()` method. You do this in `AdapterWithErrorHandler.cs` with the parameter TelemetryInitializerMiddleware telemetryInitializerMiddleware in the constructors parameter list, and the `Use(telemetryInitializerMiddleware);` statement in the contructor as shown here: ```csharp public AdapterWithErrorHandler(IConfiguration configuration, ILogger logger, TelemetryInitializerMiddleware telemetryInitializerMiddleware, ConversationState conversationState = null) : base(configuration, logger) @@ -107,9 +129,9 @@ We will start with the [CoreBot sample app](https://aka.ms/cs-core-sample) and a } ``` -6. You will also need to add `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core` to your list of using statements in `AdapterWithErrorHandler.cs`. +1. You'll also need to add `Microsoft.Bot.Builder.Integration.ApplicationInsights.Core` to your list of using statements in `AdapterWithErrorHandler.cs`. -7. Add the Application Insights instrumentation key in your `appsettings.json` file The `appsettings.json` file contains metadata about external services the Bot uses while running. For example, CosmosDB, Application Insights and the Language Understanding (LUIS) service connection and metadata is stored there. The addition to your `appsettings.json` file must be in this format: +1. Add the Application Insights instrumentation key in your `appsettings.json` file. The `appsettings.json` file contains metadata about external services the bot uses while running. For example, Cosmos DB, Application Insights, and Azure AI services connection and metadata is stored there. The addition to your `appsettings.json` file must be in this format: ```json { @@ -120,64 +142,19 @@ We will start with the [CoreBot sample app](https://aka.ms/cs-core-sample) and a } } ``` - Note: Details on getting the _Application Insights instrumentation key_ can be found in the article [Application Insights keys](../bot-service-resources-app-insights-keys.md). -At this point the preliminary work to enable telemetry using Application Insights is done. You can run your bot locally using the bot emulator and then go into Application Insights to see what is being logged such as response time, overall app health, and general running information. + > [!NOTE] + > Details on getting the _Application Insights instrumentation key_ can be found in the article [Application Insights keys](../bot-service-resources-app-insights-keys.md). -## Enabling / disabling activity event and personal information logging +At this point, the preliminary work to enable telemetry using Application Insights is done. You can run your bot locally using the Emulator and then go into Application Insights to see what is being logged, such as response time, overall app health, and general running information. -### Enabling or disabling Activity logging +### Enable telemetry in your bot's dialogs -By default, the `TelemetryInitializerMiddleware` will use the `TelemetryLoggerMiddleware` to log telemetry when your bot sends / receives activities. Activity logging creates custom event logs in your Application Insights resource. If you wish, you can disable activity event logging by setting `logActivityTelemetry` to false on the `TelemetryInitializerMiddleware` when registering it in **Startup.cs**. +When adding a new dialog to any ComponentDialog, it will inherit the Microsoft.Bot.Builder.IBotTelemetryClient of its parent dialog. For example, in the CoreBot sample application, all dialogs are added to the MainDialog, which is a ComponentDialog. Once you set the TelemetryClient property to the MainDialog, all dialogs added to it will automatically inherit the telemetryClient from it, so it doesn't need to be explicitly set when adding dialogs. -```cs -public void ConfigureServices(IServiceCollection services) -{ - ... - // Add the telemetry initializer middleware - services.AddSingleton(sp => - { - var httpContextAccessor = sp.GetService(); - var loggerMiddleware = sp.GetService(); - return new TelemetryInitializerMiddleware(httpContextAccessor, loggerMiddleware, logActivityTelemetry: false); - }); - ... -} -``` +Follow the steps below to update your CoreBot example: -The addition of `IHttpContextAccessor` will also require that you reference the Microsoft ASPNetCore HTTP library, which you achieve with the addition of this using statement: - -```cs -using Microsoft.AspNetCore.Http; -``` - -### Enable or disable logging personal information - -By default, if activity logging is enabled, some properties on the incoming / outgoing activities are excluded from logging as they are likely to contain personal information, such as user name and the activity text. You can choose to include these properties in your logging by making the following change to **Startup.cs** when registering the `TelemetryLoggerMiddleware`. - -```cs -public void ConfigureServices(IServiceCollection services) -{ - ... - // Add the telemetry initializer middleware - services.AddSingleton(sp => - { - var telemetryClient = sp.GetService(); - return new TelemetryLoggerMiddleware(telemetryClient, logPersonalInformation: true); - }); - ... -} -``` - -Next we will see what needs to be included to add telemetry functionality to the dialogs. This will enable you to get additional information such as what dialogs run, and statistics about each one. - -## Enabling telemetry in your bots Dialogs - -When adding a new dialog to any ComponentDialog, it will inherit the Microsoft.Bot.Builder.IBotTelemetryClient of its parent dialog. For example, In the CoreBot sample application all dialogs are added to the MainDialog which is a ComponentDialog. Once you set the TelemetryClient property to the MainDialog all dialogs added to it will automatically inherit the telemetryClient from it, so it does not need to be explicitly set when adding dialogs. - - Follow the steps below to update your CoreBot example: - -1. In `MainDialog.cs`, update the constructor's parameter list to include the `IBotTelemetryClient` parameter, then set the MainDialog's TelemetryClient property to that value as shown in the following code snippet: +1. In `MainDialog.cs`, update the constructor's parameter list to include the `IBotTelemetryClient` parameter, then set the MainDialog's TelemetryClient property to that value as shown in the following code snippet: ```csharp public MainDialog(IConfiguration configuration, ILogger logger, IBotTelemetryClient telemetryClient) @@ -189,500 +166,253 @@ When adding a new dialog to any ComponentDialog, it will inherit the Microsoft.B } ``` -> [!TIP] -> If you are following along and updating the CoreBot sample code, you can refer to the [Application Insights sample code](https://aka.ms/csharp-corebot-app-insights-sample) if you run into any problems. - -That's all there is to adding telemetry to your bots dialogs, at this point if you ran your bot you should see things being logged in Application Insights, however if you have any integrated technology such as LUIS and QnA Maker you will need to add the `TelemetryClient` to that code as well. - - -## Enabling telemetry to capture usage data from other services like LUIS and QnA Maker - -We will next implement telemetry functionality in your LUIS service. The LUIS service has built-in telemetry logging available so there is very little you need to do to start getting telemetry data from LUIS. If you are interested in enabling telemetry in a QnA Maker enabled bot, see [Add telemetry to your QnAMaker bot](bot-builder-telemetry-QnAMaker.md) - -1. The _`IBotTelemetryClient telemetryClient`_ parameter is required in the `FlightBookingRecognizer` constructor in `FlightBookingRecognizer.cs`: +> [!TIP] +> If you're following along and updating the CoreBot sample code, you can refer to the [Application Insights sample code](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/21.corebot-app-insights) if you run into any problems. - ```cs - public FlightBookingRecognizer(IConfiguration configuration, IBotTelemetryClient telemetryClient) - ``` +Telemetry is now added to your bot dialogs. If you run your bot now, you should see things being logged in Application Insights; however, if you have any integrated technology such as an Azure AI service, you'll need to add the `TelemetryClient` to that code as well. -2. Next you will need to enable the `telemetryClient` when creating your `LuisRecognizer` in the `FlightBookingRecognizer` constructor. You do this by adding the `telemetryClient` as a new _LuisPredictionOption_: +# [JavaScript](#tab/javascript) - ```cs - if (luisIsConfigured) - { - var luisApplication = new LuisApplication( - configuration["LuisAppId"], - configuration["LuisAPIKey"], - "https://" + configuration["LuisAPIHostName"]); +This article starts with the [CoreBot sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot) and adds the code required to integrate telemetry into any bot. This will enable Application Insights to begin tracking requests. - // Set the recognizer options depending on which endpoint version you want to use. - // More details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3 - var recognizerOptions = new LuisRecognizerOptionsV3(luisApplication) - { - TelemetryClient = telemetryClient, - }; - _recognizer = new LuisRecognizer(recognizerOptions); - } - ``` +> [!IMPORTANT] +> If you haven't setup your [Application Insights](/azure/azure-monitor/app/app-insights-overview) account and created your [Application Insights key](../bot-service-resources-app-insights-keys.md), do that before proceeding. -3. You will need to have your LUIS +1. Open the [CoreBot sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/13.core-bot) in Visual Studio Code. -That's it, you should have a functional bot that logs telemetry data into Application insights. You can use the [Bot Framework Emulator](https://aka.ms/bot-framework-emulator-readme) to run your bot locally. You shouldn't see any changes in the bot's behavior, but it will be logging information into Application Insights. Interact with the bot by sending multiple messages and in the next section we will review the telemetry results in Application Insights. +1. Add the [Application Insights key](../bot-service-resources-app-insights-keys.md) to your `.env` file: `InstrumentationKey=`. The `.env` file contains metadata about external services the bot uses while running. For example, Application Insights and Azure AI services connection and metadata is stored there. The addition to your `.env` file must be in this format: -For information on testing and debugging your bot, you can refer to the following articles: + [!code-ini[.env file](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/.env?highlight=8)] - * [Debug a bot](../bot-service-debug-bot.md) - * [Testing and debugging guidelines](bot-builder-testing-debugging.md) - * [Debug with the emulator](../bot-service-debug-emulator.md) + > [!NOTE] + > Details on getting the _Application Insights instrumentation key_ can be found in the article [Application Insights keys](../bot-service-resources-app-insights-keys.md). +1. Add a reference to the modules `ApplicationInsightsTelemetryClient` and `TelemetryInitializerMiddleware` that are located in `botbuilder-applicationinsights` in the Bot Framework SDK. To do this, add the following code starting near the top of `index.js`, just after the code to import required packages: -## Visualizing your telemetry data in Application Insights -Application Insights monitors the availability, performance, and usage of your bot application whether it's hosted in the cloud or on-premises. It leverages the powerful data analysis platform in Azure Monitor to provide you with deep insights into your application's operations and diagnose errors without waiting for a user to report them. There are a few ways to see the telemetry data collected by Application Insights, two of the primary ways are through queries and the dashboard. + [!code-javascript[Import](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/index.js?range=16-17)] -### Querying your telemetry data in Application Insights using Kusto Queries -Use this section as a starting point to learn how to use log queries in Application Insights. It demonstrates two useful queries and provides links to other documentation with additional information. + > [!TIP] + > The [JavaScript Bot Samples](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs) use Node.js, which follows the CommonJS module system, and the built in `require` function to include modules that exist in separate files. -To query your data - -1. Go to the [Azure portal](https://portal.azure.com) -2. Navigate to your Application Insights. Easiest way to do so is click on **Monitor > Applications** and find it there. -3. Once in your Application Insights, you can click on _Logs (Analytics)_ on the navigation bar. +1. Create a new function at the end of `index.js` named `getTelemetryClient` that takes your instrumentation key as a parameter and returns a _telemetry client_ using the `ApplicationInsightsTelemetryClient` module you previously referenced. This _telemetry client_ is where your telemetry data will be sent to, in this case Application Insights. - ![Logs (Analytics)](media/AppInsights-LogView.png) + [!code-javascript[getTelemetryClient](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/index.js?range=129-135)] -4. This will bring up the Query window. Enter the following query and select _Run_: - - ```sql - customEvents - | where name=="WaterfallStart" - | extend DialogId = customDimensions['DialogId'] - | extend InstanceId = tostring(customDimensions['InstanceId']) - | join kind=leftouter (customEvents | where name=="WaterfallComplete" | extend InstanceId = tostring(customDimensions['InstanceId'])) on InstanceId - | summarize starts=countif(name=='WaterfallStart'), completes=countif(name1=='WaterfallComplete') by bin(timestamp, 1d), tostring(DialogId) - | project Percentage=max_of(0.0, completes * 1.0 / starts), timestamp, tostring(DialogId) - | render timechart - ``` -5. This will return the percentage of waterfall dialogs that run to completion. +1. Next, you need to add the _telemetry middleware_ to the [adapter middleware pipeline](../v4sdk/bot-builder-concept-middleware.md#the-bot-middleware-pipeline). To do this, add the following code, starting just after the error handling code: - ![Logs (Analytics)](media/AppInsights-Query-PercentCompleteDialog.png) + [!code-javascript[telemetryClient](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/index.js?range=82-86)] +1. In order for your dialog to report telemetry data, its `telemetryClient` must match the one used for the telemetry middleware, that is, `dialog.telemetryClient = telemetryClient;` -> [!TIP] -> You can pin any query to your Application Insights dashboard by selecting the button on the top right of the **Logs (Analytics)** blade. Just select the dashboard you want it pinned to, and it will be available next time you visit that dashboard. + [!code-javascript[dialog.telemetryClient](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/index.js?range=104-109&highlight=6)] +1. After creating the restify HTTP web server object, instruct it to use the `bodyParser` handler. -## The Application Insights dashboard + [!code-javascript[dialog.telemetryClient](~/../botbuilder-samples/samples/javascript_nodejs/21.corebot-app-insights/index.js?range=125-127)] -Anytime you create an Application Insights resource in Azure, a new dashboard will automatically be created and associated with it. You can see that dashboard by selecting the button at the top of your Application Insights blade, labeled **Application Dashboard**. + > [!TIP] + > This uses the _restify_ `bodyParser` function. _restify_ is a "A Node.js web service framework optimized for building semantically correct RESTful web services ready for production use at scale. restify optimizes for introspection and performance, and is used in some of the largest Node.js deployments on Earth." For more information, see the [restify](http://restify.com) web site. -![Application Dashboard Link](media/Application-Dashboard-Link.png) + Node.js follows the CommonJS module system and uses the built-in `require` function to include modules that exist in separate files. +At this point, the preliminary work to enable telemetry using Application Insights is done. You can run your bot locally using the Emulator and then go into Application Insights to see what is being logged, such as response time, overall app health, and general running information. -Alternatively, to view the data, go to the Azure portal. Click **Dashboard** on the left, then select the dashboard you want from the drop-down. +--- -There, you'll see some default information about your bot performance and any additional queries that you've pinned to your dashboard. +## Enable or disable activity event and personal information logging +# [C#](#tab/csharp) +### Enable or disable activity logging -## Additional Information +By default, the `TelemetryInitializerMiddleware` will use the `TelemetryLoggerMiddleware` to log telemetry when your bot sends / receives activities. Activity logging creates custom event logs in your Application Insights resource. If you wish, you can disable activity event logging by setting `logActivityTelemetry` to false on the `TelemetryInitializerMiddleware` when registering it in **Startup.cs**. -* [Add telemetry to your QnAMaker bot](bot-builder-telemetry-qnamaker.md) - -* [What is Application Insights?](https://aka.ms/appinsights-overview) - -* [Using Search in Application Insights](https://aka.ms/search-in-application-insights) - -* [Create custom KPI dashboards using Azure Application Insights](https://aka.ms/custom-kpi-dashboards-application-insights) - - - - - +* [Add telemetry to your QnA Maker bot](bot-builder-telemetry-qnamaker.md) +* [What is Application Insights?](/azure/azure-monitor/app/app-insights-overview/) +* [Using Search in Application Insights](/azure/azure-monitor/app/diagnostic-search/) +* [Create custom KPI dashboards using Azure Application Insights](/azure/azure-monitor/learn/tutorial-app-dashboards/) diff --git a/articles/v4sdk/bot-builder-testing-debugging.md b/articles/v4sdk/bot-builder-testing-debugging.md index 1448af4c2..dfd62710e 100644 --- a/articles/v4sdk/bot-builder-testing-debugging.md +++ b/articles/v4sdk/bot-builder-testing-debugging.md @@ -1,58 +1,68 @@ --- title: Debugging guidelines - Bot Service -description: Understand how to debug your bot. +description: View bot debugging tips, such as using the Emulator and transcripts to inspect behavior. Understand potential middleware, state, and activity handler errors. keywords: debugging bots, botframework debugging -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 07/17/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: concept-article +ms.service: azure-ai-bot-service monikerRange: 'azure-bot-service-4.0' +ms.custom: + - evergreen --- # Debugging guidelines -[!INCLUDE[applies-to](../includes/applies-to.md)] +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Bots are complex apps, with a lot of different parts working together. Like any other complex app, this can lead to some interesting bugs or cause your bot to behave differently than expected. +Bots are complex apps, with many parts working together. Like any other complex app, this can lead to some interesting bugs or cause your bot to behave differently than expected. -Debugging, your bot can sometimes be a difficult task. Every developer has their own preferred way to accomplish that task; guidelines we present below are suggestions for you to use that apply to a large majority of bots. +Debugging, your bot can sometimes be a difficult task. Every developer has their own preferred way to accomplish that task. +The guidelines below are suggestions that apply to most bots. -After verifying your bot appears to work how you’d like it to, the next step is connecting it to a channel. To do this, you can deploy your bot to a staging server and create your own direct line client for your bot to connect to. - +After verifying that your bot works, the next step is connecting it to a channel. To do this, you can deploy your bot to a staging server, and create your own direct line client for your bot to connect to. For more information, see [Connect a bot to a Direct Line](../bot-service-channel-connect-directline.md). -Creating your own client allows you to define the inner workings of the channel, as well as specifically test how your bot responds to certain activity exchanges. Once connected to your client, run your tests to set up your bot state and verify your features. If your bot utilizes a feature like speech, using these channels can offer a way to verify that functionality. +Creating your own client allows you to define the inner workings of the channel, and test how your bot responds to certain activity exchanges. Once connected to your client, run your tests to set up your bot state and verify your features. If your bot utilizes a feature like speech, using these channels can offer a way to verify that functionality. -Use of both the Emulator and Web Chat via Azure portal here can provide further insight into how your bot performs while interacting with different channels. +> [!NOTE] +> When deploying a bot to Azure, the [Web Chat](bot-builder-webchat-overview.md) channel is provisioned by default. -Debugging your bot works similarly to other multi-threaded apps, with the ability to set breakpoints or use features like the immediate window. +Use of both the [Bot Framework Emulator](../bot-service-debug-emulator.md) and [Web Chat](bot-builder-webchat-overview.md) via Azure portal here can provide further insight into how your bot performs while interacting with different channels. -Bots follow an event driven programming paradigm, which can be hard to rationalize if you’re not familiar with it. The idea of your bot being stateless, multi-threaded, and dealing with async/await calls can result in unexpected bugs. While debugging your bot works similarly to other multi-threaded apps, we’ll cover some suggestions, tools, and resources to help. +Debugging your bot works similarly to other multi-threaded apps, with the ability to set breakpoints or use features like the immediate window. -## Understanding bot activities with the emulator +Bots follow an event driven programming paradigm, which can be hard to rationalize if you're not familiar with it. The idea of your bot being stateless, multi-threaded, and dealing with async/await calls can result in unexpected bugs. While debugging your bot works similarly to other multi-threaded apps, we'll cover some suggestions, tools, and resources to help. -Your bot deals with different types of [activities](bot-builder-basics.md#the-activity-processing-stack) besides the normal _message_ activity. Using the [emulator](../bot-service-debug-emulator.md) will show you what those activities are, when they happen, and what information they contain. Understanding those activities will help you code your bot efficiently and allows you to verify the activities your bot is sending and receiving are what you expect. +## Understanding bot activities with the Emulator + +Your bot deals with different types of [activities](bot-builder-basics.md#the-activity-processing-stack) besides the normal _message_ activity. Understanding those activities will help you code your bot efficiently and allows you to verify the activities your bot is sending and receiving are what you expect. +Using the Emulator will show you what those activities are, when they happen, and what information they contain. +For more information, see [Debug with the Emulator](../bot-service-debug-emulator.md). ## Saving and retrieving user interactions with transcripts -Azure blob transcript storage provides a specialized resource where you can both [store and retrieve transcripts](bot-builder-howto-v4-storage.md) containing interactions between your users and your bot. +Azure blob transcript storage provides a specialized resource where you can both [store and retrieve transcripts](bot-builder-howto-v4-storage.md) containing interactions between your users and your bot. Additionally, once user input interactions have been stored, you can use Azure's "_storage explorer_" to manually view data contained in transcripts stored within your blob transcript store. The following example opens "_storage explorer_" from settings for "_mynewtestblobstorage_." To open a saved user input select: Blob Container > ChannelId > TranscriptId > ConversationId -![Examine_stored_transcript_text](./media/examine_transcript_text_in_azure.png) +:::image type="content" source="./media/examine_transcript_text_in_azure.png" alt-text="Example of a transcript entry stored in a blob transcript store."::: This opens the stored user conversation input in JSON format. User input is preserved together with the key "_text:_." +For more information on creating and using a bot transcript file, see [Debug your bot using transcript files](bot-builder-debug-transcript.md). ## How middleware works -[Middleware](bot-builder-concept-middleware.md) may not be intuitive when first attempting to use it, particularly regarding the continuation, or short-circuiting, of execution. Middleware can execute on the leading or trailing edge of a turn, with a call to the `next()` delegate dictating when execution is passed to the bot logic. +[Middleware](bot-builder-concept-middleware.md) may not be intuitive when first attempting to use it, particularly regarding the continuation, or short-circuiting, of execution. Middleware can execute on the leading or trailing edge of a turn, with a call to the `next()` delegate dictating when execution is passed to the bot logic. + +If you're using multiple pieces of middleware and it's how your pipeline is oriented, the delegate may pass execution to a different piece of middleware. Details on [the bot middleware pipeline](bot-builder-concept-middleware.md#the-bot-middleware-pipeline) can help make that idea clearer. -If you are using multiple pieces of middleware the delegate may pass execution to a different piece of middleware if that is how your pipeline is oriented. Details on [the bot middleware pipeline](bot-builder-concept-middleware.md#the-bot-middleware-pipeline) can help make that idea clearer. +If the `next()` delegate isn't called, that's referred to as [short circuit routing](bot-builder-concept-middleware.md#short-circuiting). This happens when the middleware satisfies the current activity and determines it's not necessary to pass execution on. -If the `next()` delegate is not called, that’s referred to as [short circuit routing](bot-builder-concept-middleware.md#short-circuiting). This happens when the middleware satisfies the current activity and determines it’s not necessary to pass execution on. +Understanding when, and why, middleware short-circuits can help indicate which piece of middleware should come first in your pipeline. Additionally, understanding what to expect is important for built-in middleware provided by the SDK or other developers. Some find it helpful to try creating your own middleware first to experiment a bit before diving into the built-in middleware. -Understanding when, and why, middleware short-circuits helps indicate which piece of middleware should come first in your pipeline. Additionally, understanding what to expect is particularly important for built-in middleware provided by the SDK or other developers. Some find it helpful to try creating your own middleware first to experiment a bit before diving into the built-in middleware. +For more information on how to debug a bot using inspection middleware, see [Debug a bot with inspection middleware](../bot-service-debug-inspection-middleware.md). - -Sign into to the [QnA Maker portal](https://qnamaker.ai/) with your Azure credentials. - -## Create a QnA Maker service and knowledge base - -We will import an existing knowledge base definition from the QnA Maker sample in the [Microsoft/BotBuilder-Samples](https://github.com/Microsoft/BotBuilder-Samples) repo. - -1. Clone or copy the samples repo to your computer. -1. In the QnA Maker portal, **create a knowledge base**. - 1. If necessary, create a QnA service. (You can use an existing QnA Maker service or create a new one for this tutorial.) For more detailed QnA Maker instructions, see [Create a QnA Maker service](https://docs.microsoft.com/azure/cognitive-services/qnamaker/how-to/set-up-qnamaker-service-azure) and [Create, train, and publish your QnA Maker knowledge base](https://docs.microsoft.com/azure/cognitive-services/qnamaker/quickstarts/create-publish-knowledge-base). - 1. Connect your QnA service to your knowledge base. - 1. Name your knowledge base. - 1. To populate your knowledge base, use the `BotBuilder-Samples\samples\csharp_dotnetcore\11.qnamaker\CognitiveModels\smartLightFAQ.tsv` file from the samples repo. If you have downloaded the samples, upload the file *smartLightFAQ.tsv* from your computer. - 1. Click **Create your kb** to create the knowledge base. -1. **Save and train** your knowledge base. -1. **Publish** your knowledge base. - -Once your QnA Maker app is published, select the **SETTINGS** tab, and scroll down to *Deployment details*. Copy the following values from the *Postman* HTTP example request. - -```text -POST /knowledgebases//generateAnswer -Host: // NOTE - this is a URL ending in /qnamaker. -Authorization: EndpointKey -``` - -The full URL string for your hostname will look like "https://< >.azure.net/qnamaker". - -These values will be used within your `appsettings.json` or `.env` file in the next step. - -The knowledge base is now ready for your bot to use. - -## Add knowledge base information to your bot - -Beginning with bot framework v4.3 Azure no longer provides a .bot file as part of your downloaded bot source code. Use the following instructions connect your CSharp, JavaScript or Python bot to your knowledge base. - -# [C#](#tab/csharp) - -Add the following values to you appsetting.json file: - -```json -{ - "MicrosoftAppId": "", - "MicrosoftAppPassword": "", - "ScmType": "None", - - "QnAKnowledgebaseId": "knowledge-base-id", - "QnAAuthKey": "qna-maker-resource-key", - "QnAEndpointHostName": "your-hostname" // This is a URL ending in /qnamaker -} -``` - -# [JavaScript](#tab/javascript) - -Add the following values to your .env file: - -``` -MicrosoftAppId="" -MicrosoftAppPassword="" -ScmType=None - -QnAKnowledgebaseId="knowledge-base-id" -QnAAuthKey="qna-maker-resource-key" -QnAEndpointHostName="your-hostname" // This is a URL ending in /qnamaker -``` - -# [Python](#tab/python) - -Add the following values to your `config.py` file: - -```python -class DefaultConfig: - """ Bot Configuration """ - PORT = 3978 - APP_ID = os.environ.get("MicrosoftAppId", "") - APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "") - - QNA_KNOWLEDGEBASE_ID = os.environ.get("QnAKnowledgebaseId", "") - QNA_ENDPOINT_KEY = os.environ.get("QnAEndpointKey", "") - QNA_ENDPOINT_HOST = os.environ.get("QnAEndpointHostName", "") - -``` - ---- - -| Field | Value | -|:----|:----| -| QnAKnowledgebaseId | The knowledge base ID that the QnA Maker portal generated for you. | -| QnAAuthKey (QnAEndpointKey in Python) | The endpoint key that the QnA Maker portal generated for you. | -| QnAEndpointHostName | The host URL that the QnA Maker portal generated. Use the complete URL, starting with `https://` and ending with `/qnamaker`. The full URL string will look like "look like "https://< >.azure.net/qnamaker". | - -Now save your edits. - -## Update your bot to query the knowledge base - -Update your initialization code to load the service information for your knowledge base. - -# [C#](#tab/csharp) - -1. Add the **Microsoft.Bot.Builder.AI.QnA** NuGet package to your project. - - You can do this via the NuGet Package Manager or the command line: - - ```cmd - dotnet add package Microsoft.Bot.Builder.AI.QnA - ``` - - For more information on NuGet, see the [NuGet documentation](https://docs.microsoft.com/nuget/#pivot=start&panel=start-all). - -1. Add the **Microsoft.Extensions.Configuration** NuGet package to your project. - -1. In your **Startup.cs** file, add these namespace references. - - **Startup.cs** - - ```csharp - using Microsoft.Bot.Builder.AI.QnA; - using Microsoft.Extensions.Configuration; - ``` - -1. And, modify the _ConfigureServices_ method create a QnAMakerEndpoint that connects to the knowledge base defined in the **appsettings.json** file. - - **Startup.cs** - - ```csharp - // Create QnAMaker endpoint as a singleton - services.AddSingleton(new QnAMakerEndpoint - { - KnowledgeBaseId = Configuration.GetValue($"QnAKnowledgebaseId"), - EndpointKey = Configuration.GetValue($"QnAAuthKey"), - Host = Configuration.GetValue($"QnAEndpointHostName") - }); - - ``` - -1. In your **EchoBot.cs** file, add these namespace references. - - **EchoBot.cs** - - ```csharp - using System.Linq; - using Microsoft.Bot.Builder.AI.QnA; - ``` - -1. Add a `EchoBotQnA` connector and initialize it in the bot's constructor. - - **EchoBot.cs** - - ```csharp - public QnAMaker EchoBotQnA { get; private set; } - public EchoBot(QnAMakerEndpoint endpoint) - { - // connects to QnA Maker endpoint for each turn - EchoBotQnA = new QnAMaker(endpoint); - } - ``` - -1. Below the _OnMembersAddedAsync( )_ method create the method _AccessQnAMaker( )_ by adding the following code: - - **EchoBot.cs** - - ```csharp - private async Task AccessQnAMaker(ITurnContext turnContext, CancellationToken cancellationToken) - { - var results = await EchoBotQnA.GetAnswersAsync(turnContext); - if (results.Any()) - { - await turnContext.SendActivityAsync(MessageFactory.Text("QnA Maker Returned: " + results.First().Answer), cancellationToken); - } - else - { - await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken); - } - } - ``` - -1. Now within _OnMessageActivityAsync( )_ call your new method _AccessQnAMaker( )_ as follows: - - **EchoBot.cs** - - ```csharp - protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) - { - await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken); - - await AccessQnAMaker(turnContext, cancellationToken); - } - ``` - -# [JavaScript](#tab/javascript) - -1. Open a terminal or command prompt to the root directory for your project. -1. Add the **botbuilder-ai** npm package to your project. - ```shell - npm i botbuilder-ai - ``` - -1. In **index.js**, following the // Create Adapter section, add the following code to read your .env file configuration information needed to generate the QnA Maker services. - - **index.js** - ```javascript - // Map knowledge base endpoint values from .env file into the required format for `QnAMaker`. - const configuration = { - knowledgeBaseId: process.env.QnAKnowledgebaseId, - endpointKey: process.env.QnAAuthKey, - host: process.env.QnAEndpointHostName - }; - - ``` - -1. Update the bot construction to pass in the QnA services configuration information. - - **index.js** - ```javascript - // Create the main dialog. - const myBot = new MyBot(configuration, {}); - ``` - -1. In your **bot.js** file, add this require for QnAMaker - - **bot.js** - ```javascript - const { QnAMaker } = require('botbuilder-ai'); - ``` - -1. Modify the constructor to now receive passed configuration parameters required to create a QnAMaker connector and throw an error if these parameters are not provided. - - **bot.js** - ```javascript - class MyBot extends ActivityHandler { - constructor(configuration, qnaOptions) { - super(); - if (!configuration) throw new Error('[QnaMakerBot]: Missing parameter. configuration is required'); - // now create a qnaMaker connector. - this.qnaMaker = new QnAMaker(configuration, qnaOptions); - ``` - -1. Finally, update your `onMessage` function to query your knowledge bases for an answer. Pass each user input to your QnA Maker knowledge base, and return the first QnA Maker response back to the user. - - **bot.js** - - ```javascript - this.onMessage(async (context, next) => { - // send user input to QnA Maker. - const qnaResults = await this.qnaMaker.getAnswers(context); - - // If an answer was received from QnA Maker, send the answer back to the user. - if (qnaResults[0]) { - await context.sendActivity(`QnAMaker returned response: ' ${ qnaResults[0].answer}`); - } - else { - // If no answers were returned from QnA Maker, reply with help. - await context.sendActivity('No QnA Maker response was returned.' - + 'This example uses a QnA Maker Knowledge Base that focuses on smart light bulbs. ' - + `Ask the bot questions like "Why won't it turn on?" or "I need help."`); - } - await next(); - }); - ``` - -# [Python](#tab/python) - -1. Assure that you have installed the packages as described in the samples repository README file. -1. Add the `botbuilder-ai` reference to the `requirements.txt` file, as shown below. - - **requirements.txt** - - ```text - botbuilder-core - botbuilder-ai - flask - ``` - - Notice that the versions may vary. - -1. In the `app.py` file modify the bot instance creation as shown below. - - **app.py** - - ```python - # Create the main dialog - BOT = MyBot(APP.config) - ``` - -1. In the `bot.py` file import `QnAMaker` and `QnAMakerEndpoint`; also import `Config`, as shown below. - - **bot.py** - - ```python - from flask import Config - - from botbuilder.ai.qna import QnAMaker, QnAMakerEndpoint - from botbuilder.core import ActivityHandler, MessageFactory, TurnContext - from botbuilder.schema import ChannelAccount - ``` - -1. Add a __init__ function to instantiate a `qna-maker` object. using the configuration parameters provided in the `config.py` file. - - **bot.py** - - ```python - def __init__(self, config: Config): - self.qna_maker = QnAMaker( - QnAMakerEndpoint( - knowledge_base_id=config["QNA_KNOWLEDGEBASE_ID"], - endpoint_key=config["QNA_ENDPOINT_KEY"], - host=config["QNA_ENDPOINT_HOST"], - ) - ) - - ``` - -1. Update `on_message_activity` to query your knowledge base for an answer. Pass each user input to your QnA Maker knowledge base, and return the first QnA Maker response to the user. - - **bot.py** - - ```python - async def on_message_activity(self, turn_context: TurnContext): - # The actual call to the QnA Maker service. - response = await self.qna_maker.get_answers(turn_context) - if response and len(response) > 0: - await turn_context.send_activity(MessageFactory.text(response[0].answer)) - else: - await turn_context.send_activity("No QnA Maker answers were found.") - - ``` - -1. Optionally, update the welcome message in `on_members_added_activity` for example: - - **bot.py** - - ```python - await turn_context.send_activity("Hello and welcome to QnA!") - ``` - ---- - -### Test the bot locally - -At this point your bot should be able to answer some questions. Run the bot locally and open it in the Emulator. - -![test qna sample](./media/qna-test-bot.png) - -## Republish your bot -You can now republish your bot back to Azure. You need to zip your project folder and then run the command to deploy your bot to Azure. For details please read the [deploy a bot](https://docs.microsoft.com/azure/bot-service/bot-builder-deploy-az-cli?view=azure-bot-service-4.0&tabs=csharp) article. - -### Zip your project folder -[!INCLUDE [zip up code](~/includes/deploy/snippet-zip-code.md)] - - - -### Deploy your code to Azure -[!INCLUDE [deploy code to Azure](~/includes/deploy/snippet-deploy-code-to-az.md)] - - - -If you're not going to continue to use this application, delete -the associated resources with the following steps: - -1. In the Azure portal, open the resource group for your bot. -2. Click **Delete resource group** to delete the group and all the resources it contains. -3. In the confirmation pane, enter the resource group name, and click **Delete**. - -## Next steps - -For information on how to add features to your bot, see the **Send and receive text message** article and the other articles in the how-to develop section. -> [!div class="nextstepaction"] -> [Send and receive text message](bot-builder-howto-send-messages.md) diff --git a/articles/v4sdk/bot-builder-tutorial-basic-deploy.md b/articles/v4sdk/bot-builder-tutorial-basic-deploy.md deleted file mode 100644 index 28cedf741..000000000 --- a/articles/v4sdk/bot-builder-tutorial-basic-deploy.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Tutorial to create and deploy a basic bot - Bot Service -description: Learn how to create a basic bot and then deploy it to Azure. -keywords: echo bot, deploy, azure, tutorial -author: Ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/28/2020 -monikerRange: 'azure-bot-service-4.0' ---- - -# Tutorial: Create and deploy a basic bot - -[!INCLUDE [applies-to-v4](../includes/applies-to.md)] - -This tutorial walks you through creating a basic bot with the Bot Framework SDK and deploying it to Azure. If you've already created a basic bot and have it running locally, skip ahead to the [Deploy your bot](#deploy-your-bot) section. - -In this tutorial, you learn how to: - -> [!div class="checklist"] -> * Create a basic Echo bot -> * Run and interact with it locally -> * Publish your bot - -If you don’t have an Azure subscription, create a [free account](https://azure.microsoft.com/free/?WT.mc_id=A261C142F) before you begin. - -# [C#](#tab/csharp) - -[!INCLUDE [dotnet quickstart](~/includes/quickstart-dotnet.md)] - -# [JavaScript](#tab/javascript) - -[!INCLUDE [javascript quickstart](~/includes/quickstart-javascript.md)] - -# [Python](#tab/python) - -[!INCLUDE [python quickstart](~/includes/quickstart-python.md)] - ---- - -## Deploy your bot - -### Prerequisites -[!INCLUDE [deploy prerequisite](~/includes/deploy/snippet-prerequisite.md)] - -### Prepare for deployment - -> [!TIP] -> This procedure uses a ZIP file to deploy your bot. In C#, this may fail if the solution configuration at build is set to **Debug**. -> In Visual Studio, make sure that the solution configuration is set to **Release** and perform a clean rebuild of the solution before continuing. - -[!INCLUDE [deploy prepare intro](~/includes/deploy/snippet-prepare-deploy-intro.md)] - -#### 1. Login to Azure -[!INCLUDE [deploy az login](~/includes/deploy/snippet-az-login.md)] - -#### 2. Set the subscription -[!INCLUDE [deploy az subscription](~/includes/deploy/snippet-az-set-subscription.md)] - -#### 3. Create an App registration -[!INCLUDE [deploy create app registration](~/includes/deploy/snippet-create-app-registration.md)] - -#### 4. Deploy via ARM template -You can deploy your bot in a new resource group or an existing resource group. Choose the option that works best for you. - -> [!NOTE] -> Python bots cannot be deployed to a resource group that contains Windows services/bots. Multiple Python bots can be deployed to the same resource group, but create other services (LUIS, QnA, etc.) in another resource group. -> - -##### **Deploy via ARM template with new Resource Group** -[!INCLUDE [ARM with new resourece group](~/includes/deploy/snippet-ARM-new-resource-group.md)] - -##### **Deploy via ARM template with existing Resource Group** -[!INCLUDE [ARM with existing resourece group](~/includes/deploy/snippet-ARM-existing-resource-group.md)] - -#### 5. Prepare your code for deployment -##### **Retrieve or create necessary IIS/Kudu files** -[!INCLUDE [retrieve or create IIS/Kudu files](~/includes/deploy/snippet-IIS-Kudu-files.md)] - -##### **Zip up the code directory manually** -[!INCLUDE [zip up code](~/includes/deploy/snippet-zip-code.md)] - -### Deploy code to Azure -[!INCLUDE [deploy code to Azure](~/includes/deploy/snippet-deploy-code-to-az.md)] - -### Test in Web Chat -[!INCLUDE [test in web chat](~/includes/deploy/snippet-test-in-web-chat.md)] - -## Additional resources - -[!INCLUDE [additional resources snippet](~/includes/deploy/snippet-additional-resources.md)] - -## Next steps -> [!div class="nextstepaction"] -> [Use QnA Maker in your bot to answer questions](bot-builder-tutorial-add-qna.md) \ No newline at end of file diff --git a/articles/v4sdk/bot-builder-tutorial-dispatch.md b/articles/v4sdk/bot-builder-tutorial-dispatch.md deleted file mode 100644 index 649b8089f..000000000 --- a/articles/v4sdk/bot-builder-tutorial-dispatch.md +++ /dev/null @@ -1,593 +0,0 @@ ---- -title: Use multiple LUIS and QnA models - Bot Service -description: Learn how to use LUIS and QnA maker in your bot. -keywords: Luis, QnA, Dispatch tool, multiple services, route intents -author: diberry -ms.author: diberry -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 01/27/2020 -monikerRange: 'azure-bot-service-4.0' ---- - -# Use multiple LUIS and QnA models - -[!INCLUDE[applies-to](../includes/applies-to.md)] - -If a bot uses multiple LUIS models and QnA Maker knowledge bases (knowledge bases), you can use Dispatch tool to determine which LUIS model or QnA Maker knowledge base best matches the user input. The dispatch tool does this by creating a single LUIS app to route user input to the correct model. For more information about the Dispatch, including the CLI commands, refer to the [README][dispatch-readme]. - -## Prerequisites - -- Knowledge of [bot basics](bot-builder-basics.md), [LUIS][howto-luis], and [QnA Maker][howto-qna]. -- [Dispatch tool](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/Dispatch) -- A copy of the **NLP with Dispatch** from the [C# Sample][cs-sample], [JS Sample][js-sample], or [Python Sample][python-sample] code repository. -- A [luis.ai](https://www.luis.ai/) account to publish LUIS apps. -- A [QnA Maker](https://www.qnamaker.ai/) account to publish the QnA knowledge base. - -## About this sample - -This sample is based on a predefined set of LUIS and QnA Maker Apps. - -## [C#](#tab/cs) - -![Code sample logic flow](./media/tutorial-dispatch/dispatch-logic-flow.png) - -`OnMessageActivityAsync` is called for each user input received. This module finds the top scoring user intent and passes that result on to `DispatchToTopIntentAsync`. DispatchToTopIntentAsync, in turn, calls the appropriate app handler - -- `ProcessSampleQnAAsync` - for bot faq questions. -- `ProcessWeatherAsync` - for weather queries. -- `ProcessHomeAutomationAsync` - for home lighting commands. - -## [JavaScript](#tab/js) - -![Code sample logic flow](./media/tutorial-dispatch/dispatch-logic-flow-js.png) - -`onMessage` is called for each user input received. This module finds the top scoring user intent and passes that result on to `dispatchToTopIntentAsync`. dispatchToTopIntentAsync, in turn, calls the appropriate app handler - -- `processSampleQnA` - for bot faq questions. -- `processWeather` - for weather queries. -- `processHomeAutomation` - for home lighting commands. - -## [Python](#tab/python) - -![Code sample logic flow](./media/tutorial-dispatch/dispatch-logic-flow-python.png) - -`on_message_activity` is called for each user input received. This module finds the top scoring user intent and passes that result on to `_dispatch_to_top_intent`. _dispatch_to_top_intent, in turn, calls the appropriate app handler - -- `_process_sample_qna` - for bot faq questions. -- `_process_weather` - for weather queries. -- `_process_home_automation` - for home lighting commands. - ---- - -The handler calls the LUIS or QnA Maker service and returns the generated result back to the user. - -## Create LUIS apps and QnA knowledge base - -Before you can create the dispatch model, you'll need to have your LUIS apps and QnA knowledge bases created and published. In this article, we'll publish the following models that are included with the _NLP With Dispatch_ sample in the `\CognitiveModels` folder: - -| Name | Description | -|------|------| -| HomeAutomation | A LUIS app that recognizes a home automation intent with associated entity data.| -| Weather | A LUIS app that recognizes weather-related intents with location data.| -| QnAMaker | A QnA Maker knowledge base that provides answers to simple questions about the bot. | - -### Create LUIS apps - -1. Log into the [LUIS web portal](https://www.luis.ai/). Under the _My apps_ section, select the Tab _Import new app_. The following Dialog Box will appear: - - ![Import LUIS json file](./media/tutorial-dispatch/import-new-luis-app.png) - -2. Select the button _Choose app file_, navigate to the CognitiveModel folder of your sample code and select the file 'HomeAutomation.json'. Leave the optional name field blank. - -3. Select _Done_. - -4. Once LUIS opens up your Home Automation app, select the _Train_ button. This will train your app using the set of utterances you just imported using the 'home-automation.json' file. - -5. When training is complete, select the _Publish_ button. The following Dialog Box will appear: - - ![Publish LUIS app](./media/tutorial-dispatch/publish-luis-app.png) - -6. Choose the 'production' environment and then select the _Publish_ button. - -7. Once your new LUIS app has been published, select the _MANAGE_ Tab. From the 'Application Information' page, record the values `Application ID` as "_app-id-for-app_" and `Display name` as "_name-of-app_". From the 'Key and Endpoints' page, record the values `Authoring Key` as "_your-luis-authoring-key_" and `Region` as "_your-region_". These values will later be used within your 'appsetting.json' file. - -8. Once completed, _Train_ and _Publish_ both your LUIS **Home Automation** app and your LUIS **Weather** app by repeating the above steps for 'Weather.json' file. - -### Create QnA Maker knowledge base - -The first step to setting up a QnA Maker knowledge base is to set up a QnA Maker service in Azure. To do that, follow the step-by-step instructions found [here](https://aka.ms/create-qna-maker). - -Once your QnA Maker Service has been created in Azure, you need to record the Cognitive Services _Key 1_ provided for your QnA Maker service. This will be used as \ when adding the QnA Maker app to your dispatch application. - -Learn more about the [two different types of keys](https://docs.microsoft.com/azure/cognitive-services/qnamaker/how-to/set-up-qnamaker-service-azure#types-of-keys-in-qna-maker) used with QnA Maker. - -The following steps provide you with this key: - -![Select Cognitive Service](./media/tutorial-dispatch/select-qna-cognitive-service.png) - -1. From within your Azure portal, select your QnA Maker cognitive service. - - ![Select Cognitive Service Keys](./media/tutorial-dispatch/select-cognitive-service-keys.png) - -1. Select the Keys icon found under the _Resource Management_ section on the left-hand menu. - - ![Select Cognitive Service Key1](./media/tutorial-dispatch/select-cognitive-service-key1.png) - -1. Copy the value of _Key 1_ to your clipboard and save this locally. this will later be used for the (-k) key value \ when adding the QnA Maker app to your dispatch application. - -1. Now sign in to the [QnAMaker web portal](https://qnamaker.ai). - -1. At step 2, select the following: - - - Your Azure AD account. - - Your Azure subscription name. - - The name you created for your QnA Maker service. (If your Azure QnA service does not initially appear in this pull down list, try refreshing the page.) - - ![Create QnA Step 2](./media/tutorial-dispatch/create-qna-step-2.png) - -1. At step 3, provide a name for your QnA Maker knowledge base. For this example use the name 'sample-qna'. - - ![Create QnA Step 3](./media/tutorial-dispatch/create-qna-step-3.png) - -1. At step 4, select the option _+ Add File_, navigate to the CognitiveModel folder of your sample code, and select the file 'QnAMaker.tsv'. There is an additional selection to add a _Chit-chat_ personality to your knowledge base but our example does not include this option. - - ![Create QnA Step 4](./media/tutorial-dispatch/create-qna-step-4.png) - -1. At step 5, select _Create your knowledge base_. - -1. Once the knowledge base is created from your uploaded file, select _Save and train_ and when finished, select the _PUBLISH_ Tab and publish your app. - -1. Once your QnA Maker app is published, select the _SETTINGS_ Tab, and scroll down to 'Deployment details'. Record the following values from the _Postman_ Sample HTTP request. - - ```text - POST /knowledge bases//generateAnswer - Host: // NOTE - this is a URL. - Authorization: EndpointKey - ``` - - The full URL string for your hostname will look like "https://.azure.net/qnamaker". These values will later be used within your `appsettings.json` or `.env` file. - -## Dispatch app needs read access to existing apps - -The dispatch tool needs authoring access to read the existing LUIS and QnA Maker apps in order to create a new parent LUIS app that dispatches to the LUIS and QnA Maker apps. This access is provided with the app IDs and authoring keys. - -### Service authoring keys - -The **authoring key** is only used for creating and editing the models. You need an ID and key for each of the two LUIS apps and the QnA Maker app. - -|App|Location of information| -|--|--| -|LUIS|**App ID** - found in the [LUIS portal](https://www.luis.ai) for each app, Manage -> Application Information
**Authoring Key** - found in the LUIS portal, top-right corner, select your own User, then Settings.| -|QnA Maker| **App ID** - found in the [QnA Maker portal](https://http://qnamaker.ai) on the Settings page after you publish the app. This is the ID found in first part of the POST command after the knowledgebase. An example of where to find the app ID is `POST /knowledgebases//generateAnswer`.
**Authoring Key** - found in the Azure portal, for the QnA Maker resource, under the **Keys**. You only need one of the keys.| - -The authoring key is not used to get a prediction score or confidence score from the published application. You need the endpoint keys for this action. The **[endpoint keys](#service-endpoint-keys)** are found and used later in this tutorial. - -Learn more about the [two different types of keys](https://docs.microsoft.com/azure/cognitive-services/qnamaker/how-to/set-up-qnamaker-service-azure#types-of-keys-in-qna-maker) used with QnA Maker. - -## Create the dispatch model - -The CLI interface for the dispatch tool creates the model for dispatching to the correct LUIS or QnA Maker app. - -1. Open a command prompt or terminal window, and change directories to the **CognitiveModels** directory -1. Make sure you have the current version of npm and the Dispatch tool. - - ```cmd - npm i -g npm - npm i -g botdispatch - ``` - -1. Use `dispatch init` to initialize create a `.dispatch` file for your dispatch model. Create this using a filename you will recognize. - - ```cmd - dispatch init -n --luisAuthoringKey "" --luisAuthoringRegion - ``` - -1. Use `dispatch add` to add your LUIS apps and QnA Maker knowledge bases to the `.dispatch` file. - - ```cmd - dispatch add -t luis -i "" -n "" -v -k "" --intentName l_Weather - dispatch add -t luis -i "" -n "" -v -k "" --intentName l_HomeAutomation - dispatch add -t qna -i "" -n "" -k "" --intentName q_sample-qna - ``` - -1. Use `dispatch create` to generate a dispatch model from the `.dispatch` file. - - ```cmd - dispatch create - ``` - -1. Publish the dispatch LUIS app, just created. - -## Use the dispatch LUIS app - -The generated LUIS app defines intents for each of the child apps and the knowledge base, as well as a _none_ intent for when the utterance doesn't have a good fit. - -- `l_HomeAutomation` -- `l_Weather` -- `None` -- `q_sample-qna` - -These services need to be published under the correct names for the bot to run properly. -The bot needs information about the published services, so that it can access those services. - -### Service endpoint keys - -The bot needs the query prediction endpoints for the three LUIS apps (dispatch, weather, and home automation) and the single QnA Maker knowledge base. Use the following table to find the endpoint keys: - -|App|Query endpoint key location| -|--|--| -|LUIS|In the LUIS portal, for each LUIS app, in the Manage section, select **Keys and Endpoint settings** to find the keys associated with each app. If you are following this tutorial, the endpoint key is the same key as the ``. The authoring key allows for 1000 endpoint hits then expires.| -|QnA Maker|In the QnA Maker portal, for the knowledge base, in the Manage settings, use the key value shows in the Postman settings for the **Authorization** header, without the text of `EndpointKey`.| - -These values are used in the **appsettings.json** for C# and the **.env** file for javascript. - -## [C#](#tab/cs) - -### Installing packages - -Prior to running this app for the first time ensure that several NuGet packages are installed: - -- **Microsoft.Bot.Builder** -- **Microsoft.Bot.Builder.AI.Luis** -- **Microsoft.Bot.Builder.AI.QnA** - -### Manually update your appsettings.json file - -Once all of your service apps are created, the information for each needs to be added into your 'appsettings.json' file. The initial [C# Sample][cs-sample] code contains an empty appsettings.json file: - -**appsettings.json** - -[!code-json[AppSettings](~/../botbuilder-samples/samples/csharp_dotnetcore/14.nlp-with-dispatch/AppSettings.json?range=8-17)] - -For each of the entities shown below, add the values you recorded earlier in these instructions: - -**appsettings.json** - -```json -"MicrosoftAppId": "", -"MicrosoftAppPassword": "", - -"QnAKnowledgebaseId": "", -"QnAEndpointKey": "", -"QnAEndpointHostName": "", - -"LuisAppId": "", -"LuisAPIKey": "", -"LuisAPIHostName": "", -``` - -When all changes are complete, save this file. - -## [JavaScript](#tab/js) - -### Installing packages - -Prior to running this app for the first time you will need to install several npm packages. - -```powershell -npm install --save botbuilder -npm install --save botbuilder-ai -``` - -To use the .env configuration file, your bot needs an extra package included: - -```powershell -npm install --save dotenv -``` - -### Manually update your .env file - -Once all of your service apps are created, the information for each needs to be added into your '.env' file. The initial [JavaScript Sample][js-sample] code contains an empty .env file. - -**.env** -[!code-file[EmptyEnv](~/../botbuilder-samples/samples/javascript_nodejs/14.nlp-with-dispatch/.env?range=1-10)] - -Add your service connection values as shown below: - -**.env** - -```file -MicrosoftAppId="" -MicrosoftAppPassword="" - -QnAKnowledgebaseId="" -QnAEndpointKey="" -QnAEndpointHostName="" - -LuisAppId= -LuisAPIKey= -LuisAPIHostName= -``` - -When all changes are in place, save this file. - -## [Python](#tab/python) - -### Installing packages - -Prior to running this app for the first time you will need to install several PyPI packages. - -```powershell -pip install azure -pip install botbuilder-core -pip install botbuilder-ai -``` - -### Manually update your config.py file -Once all of your service apps are created, the information for each needs to be added into your 'config.py' file. The initial [Python Sample][python-sample] code contains an empty config.py file. - -**config.py** - -[!code-python[config.py](~/../botbuilder-samples/samples/python/14.nlp-with-dispatch/config.py?range=10-24)] - -For each of the entities shown below, add the values you recorded earlier in these instructions: - -```python -APP_ID = os.environ.get("MicrosoftAppId", "") -APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "") - -QNA_KNOWLEDGEBASE_ID = os.environ.get("QnAKnowledgebaseId", "") -QNA_ENDPOINT_KEY = os.environ.get("QnAEndpointKey", "") -QNA_ENDPOINT_HOST = os.environ.get("QnAEndpointHostName", "") - -LUIS_APP_ID = os.environ.get("LuisAppId", "") -LUIS_API_KEY = os.environ.get("LuisAPIKey", "") -# LUIS endpoint host name, ie "westus.api.cognitive.microsoft.com" -LUIS_API_HOST_NAME = os.environ.get("LuisAPIHostName", "") -``` - -When all changes are complete, save this file. - ---- - -### Connect to the services from your bot - -To connect to the Dispatch, LUIS, and QnA Maker services, your bot pulls information from the settings file. - -## [C#](#tab/cs) - -In **BotServices.cs**, the information contained within configuration file _appsettings.json_ is used to connect your dispatch bot to the `Dispatch` and `SampleQnA` services. The constructors use the values you provided to connect to these services. - -**BotServices.cs** - -[!code-csharp[ReadConfigurationInfo](~/../botbuilder-samples/samples/csharp_dotnetcore/14.nlp-with-dispatch/BotServices.cs?range=14-45)] - -## [JavaScript](#tab/js) - -In **dispatchBot.js** the information contained within configuration file _.env_ is used to connect your dispatch bot to the _LuisRecognizer(dispatch)_ and _QnAMaker_ services. The constructors use the values you provided to connect to these services. - -**bots/dispatchBot.js** - -[!code-javascript[ReadConfigurationInfo](~/../botbuilder-samples/samples/javascript_nodejs/14.nlp-with-dispatch/bots/dispatchBot.js?range=11-26)] - -## [Python](#tab/python) - -In **dispatch_bot.py**, the information contained within configuration file _config.py_ is used to connect your dispatch bot to the _QnAMaker_ and _LuisRecognizer_ services. The constructors use the values you provided to connect to these services. - -**bots/dispatch_bot.py** - -[!code-python[ReadConfigurationInfo](~/../botbuilder-samples/samples/python/14.nlp-with-dispatch/bots/dispatch_bot.py?range=14-34)] - ---- - -> [!NOTE] -> By default the `includeApiResults` parameter is set to false, meaning the recognizer will only return basic information about entities / intents. If you require the full response from LUIS (such as the `ConnectedServiceResult` used later in this tutorial), then set this parameter to true. This will then add the full response from the LUIS service into the Properties collection on the `RecognizerResult`. - -### Call the services from your bot - -For each input from your user, the bot logic checks user input against the combined Dispatch model, finds the top returned intent, and uses that information to call the appropriate service for the input. - -## [C#](#tab/cs) - -In the **DispatchBot.cs** file whenever the `OnMessageActivityAsync` method is called, we check the incoming user message against the Dispatch model. We then pass the Dispatch Model's `topIntent` and `recognizerResult` on to the correct method to call the service and return the result. - -**bots\DispatchBot.cs** - -[!code-csharp[OnMessageActivity](~/../botbuilder-samples/samples/csharp_dotnetcore/14.nlp-with-dispatch/bots/DispatchBot.cs?range=26-36)] - -## [JavaScript](#tab/js) - -In the **dispatchBot.js** `onMessage` method, we check the user input message against the Dispatch model, find the _topIntent_, then pass this on by calling _dispatchToTopIntentAsync_. - -[!code-javascript[onMessage](~/../botbuilder-samples/samples/javascript_nodejs/14.nlp-with-dispatch/bots/dispatchBot.js?range=31-44)] - -## [Python](#tab/python) - -In the **dispatch_bot.py** file whenever the `on_message_activity` method is called, we check the incoming user message against the Dispatch model. We then pass the Dispatch Model's `top_intent` and `recognize_result` on to the correct method to call the service and return the result. - -**bots/dispatch_bot.py** - -[!code-python[on_message](~/../botbuilder-samples/samples/python/14.nlp-with-dispatch/bots/dispatch_bot.py?range=46-54)] - ---- - -### Work with the recognition results - -## [C#](#tab/cs) - -When the model produces a result, it indicates which service can most appropriately process the utterance. The code in this bot routes the request to the corresponding service, and then summarizes the response from the called service. Depending on the _intent_ returned from Dispatch, this code uses the returned intent to route to the correct LUIS model or QnA service. - -**bots\DispatchBot.cs** - -[!code-csharp[DispatchToTop](~/../botbuilder-samples/samples/csharp_dotnetcore/14.nlp-with-dispatch/bots/DispatchBot.cs?range=51-69)] - -If method `ProcessHomeAutomationAsync` or `ProcessWeatherAsync` are invoked, they are passed the results from the dispatch model within _luisResult.ConnectedServiceResult_. The specified method then provides user feedback showing the dispatch model top intent, plus a ranked listing of all intents and entities that were detected. - -If method `q_sample-qna` is invoked, it uses the user input contained within the turnContext to generate an answer from the knowledge base and display that result to the user. - -## [JavaScript](#tab/js) - -When the model produces a result, it indicates which service can most appropriately process the utterance. The code in this sample uses the recognized _topIntent_ to show how to route the request on to the corresponding service. - -**bots/dispatchBot.js** -[!code-javascript[dispatchToTopIntentAsync](~/../botbuilder-samples/samples/javascript_nodejs/14.nlp-with-dispatch/bots/dispatchBot.js?range=61-77)] - -If method `processHomeAutomation` or `processWeather` are invoked, they are passed the results from the dispatch model within _recognizerResult.luisResult_. The specified method then provides user feedback showing the dispatch model's top intent, plus a ranked listing of all intents and entities that were detected. - -If method `q_sample-qna` is invoked, it uses the user input contained within the turnContext to generate an answer from the knowledge base and display that result to the user. - -## [Python](#tab/python) - -When the model produces a result, it indicates which service can most appropriately process the utterance. The code in this sample uses the recognized top _intent_ to show how to route the request on to the corresponding service. - -**bots\dispatch_bot.py** - -[!code-python[dispatch top intent](~/../botbuilder-samples/samples/python/14.nlp-with-dispatch/bots/dispatch_bot.py?range=56-70)] - -If method `_process_home_automation` or `_process_weather` are invoked, they are passed the results from the dispatch model within _recognizer_result.properties["luisResult"]_. The specified method then provides user feedback showing the dispatch model top intent, plus a ranked listing of all intents and entities that were detected. - -If method `q_sample-qna` is invoked, it uses the user input contained within the turnContext to generate an answer from the knowledge base and display that result to the user. - ---- - -> [!NOTE] -> If this were a production application, this is where the selected LUIS methods would connect to their specified service, pass in the user input, and process the returned LUIS intent and entity data. - -## Test your bot - -1. Using your development environment, start the sample code. Note the _localhost_ address shown in the address bar of the browser window opened by your App: "https://localhost:". -1. Open your Bot Framework Emulator, then select `Create a new bot configuration`. A `.bot` file enables you to use the _Inspector_ in the bot emulator to see the JSON returned from LUIS and QnA Maker. -1. In the **New bot configuration** dialog box, enter your bot name, and your endpoint URL, such as `http://localhost:3978/api/messages`. Save the file at the root of your bot sample code project. -1. Open the bot file and add sections for your LUIS and QnA Maker apps. Use [this example file](https://github.com/microsoft/botbuilder-tools/blob/master/packages/MSBot/docs/sample-bot-file.json) as a template for settings. Save the changes. -1. Select the bot name in the **My Bots** list to access your running bot. For your reference, here are some of the questions and commands that are covered by the services built for your bot: - - - QnA Maker - - `hi`, `good morning` - - `what are you`, `what do you do` - - LUIS (home automation) - - `turn on bedroom light` - - `turn off bedroom light` - - `make some coffee` - - LUIS (weather) - - `whats the weather in redmond washington` - - `what's the forecast for london` - - `show me the forecast for nebraska` - -## Dispatch for user utterance to QnA Maker - -1. In the bot emulator, enter the text `hi` and submit the utterance. The bot submits this query to the dispatch LUIS app and gets back a response indicating which child app should get this utterance for further processing. - -1. By selecting the `LUIS Trace` line in the log, you can see the LUIS response in the bot emulator . The LUIS result from the dispatch LUIS app displays in the Inspector. - - ```json - { - "luisResponse": { - "entities": [], - "intents": [ - { - "intent": "q_sample-qna", - "score": 0.9489713 - }, - { - "intent": "l_HomeAutomation", - "score": 0.0612499453 - }, - { - "intent": "None", - "score": 0.008567564 - }, - { - "intent": "l_Weather", - "score": 0.0025761195 - } - ], - "query": "Hi", - "topScoringIntent": { - "intent": "q_sample-qna", - "score": 0.9489713 - } - } - } - ``` - - Because the utterance, `hi`, is part of the dispatch LUIS app's **q_sample-qna** intent, and is selected as the `topScoringIntent`, the bot will make a second request, this time to the QnA Maker app, with the same utterance. - -1. Select the `QnAMaker Trace` line in the bot emulator log. The QnA Maker result displays in the Inspector. - -```json -{ - "questions": [ - "hi", - "greetings", - "good morning", - "good evening" - ], - "answer": "Hello!", - "score": 1, - "id": 96, - "source": "QnAMaker.tsv", - "metadata": [], - "context": { - "isContextOnly": false, - "prompts": [] - } -} -``` - -## Resolving incorrect top intent from Dispatch - -Once your bot is running, it is possible to improve the bot's performance by removing similar or overlapping utterances between the dispatched apps. - -You can use the [Dispatch][dispatch-readme] command-line tool to test and evaluate your dispatch model. - -### To update or create a new LUIS model - -This sample is based on a preconfigured LUIS model. Additional information to help you update this model, or create a new LUIS model, can be found [here](https://aka.ms/create-luis-model#updating-your-cognitive-models). - -After updating the underlying models (QnA or LUIS) run `dispatch refresh` to update your Dispatch LUIS app. `dispatch refresh` is basically the same command as `dispatch create` except no new LUIS app ID is created. - -Note that utterances that were added directly in LUIS will not be retained when running `dispatch refresh`. To keep those extra utterances in the Dispatch app add those utterances in a text file (one utterance per line), and then add the file to Dispatch by running the command: - -```powershell -dispatch add -t file -f --intentName -``` - -Once the file with extra utterances is added to Dispatch the utterances will stay with every refresh. - -### To delete resources - -This sample creates a number of applications and resources that you can delete using the steps listed below, but you should not delete resources that *any other apps or services* rely on. - -To delete LUIS resources: - -1. Sign in to the [luis.ai](https://www.luis.ai) portal. -1. Go to the _My Apps_ page. -1. Select the apps created by this sample. - - `Home Automation` - - `Weather` - - `NLP-With-Dispatch-BotDispatch` -1. Click _Delete_, and click _Ok_ to confirm. - -To delete QnA Maker resources: - -1. Sign in to the [qnamaker.ai](https://www.qnamaker.ai/) portal. -1. Go to the _My knowledge bases_ page. -1. Click the delete button for the `Sample QnA` knowledge base, and click _Delete_ to confirm. - -### Best practice - -To improve services used in this sample, refer to best practice for [LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-best-practices), and [QnA Maker](https://docs.microsoft.com/azure/cognitive-services/qnamaker/concepts/best-practices). - - - - - -[howto-luis]: bot-builder-howto-v4-luis.md -[howto-qna]: bot-builder-howto-qna.md - -[cs-sample]: https://aka.ms/dispatch-sample-cs -[js-sample]: https://aka.ms/dispatch-sample-js -[python-sample]: https://aka.ms/dispatch-sample-python - -[dispatch-readme]: https://aka.ms/dispatch-command-line-tool - diff --git a/articles/v4sdk/bot-builder-tutorial-orchestrator.md b/articles/v4sdk/bot-builder-tutorial-orchestrator.md new file mode 100644 index 000000000..2d30aea44 --- /dev/null +++ b/articles/v4sdk/bot-builder-tutorial-orchestrator.md @@ -0,0 +1,423 @@ +--- +title: Use multiple LUIS and QnA Maker projects with Orchestrator +description: Learn how bots can use multiple LUIS models and QnA Maker knowledge bases. See how to use Orchestrator to route user input to the correct model. +keywords: Luis, QnA, Orchestrator, multiple services, route intents +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +monikerRange: 'azure-bot-service-4.0' +ROBOTS: NOINDEX +ms.custom: + - evergreen +--- + +# Use multiple LUIS and QnA models with Orchestrator + +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +[!INCLUDE [luis-sunset-alert](../includes/luis-sunset-alert.md)] + +If a bot uses multiple Language Understanding (LUIS) models and QnA Maker knowledge bases, you can use Bot Framework Orchestrator to determine which LUIS model or QnA Maker knowledge base best matches the user input. You can use the `bf orchestrator` CLI command to create an Orchestrator snapshot file, then use the snapshot file to route user input to the correct model at run time. + +This article describes how to use an _existing_ QnA Maker knowledge base with Orchestrator. + +- For new bots, consider using the [question answering](bot-builder-concept-luis.md#question-answering) and [orchestration workflow](bot-builder-concept-luis.md#use-orchestration-workflow) features of Azure AI Language. +- For more information about Orchestrator, see [Intent recognition with Orchestrator in Composer][orchestrator]. +- For more information about the `bf orchestrator` command, see [the Bot Framework CLI README][bf-orchestrator-cli]. + +## Prerequisites + +- A [luis.ai](https://www.luis.ai/) account to author LUIS apps. +- A [QnA Maker](https://www.qnamaker.ai/) account and an existing QnA Maker knowledge base. +- A copy of the **NLP with Orchestrator** sample in [**C#** (archived)][] or [**JavaScript** (archived)][]. +- Knowledge of [bot basics](bot-builder-basics.md), [LUIS][howto-luis], and [QnA Maker][howto-qna]. +- Install the command-line [BF CLI][bf-cli]. + + + +## About this sample + +This sample is based on a predefined set of LUIS and QnA Maker projects. +However, to use QnA Maker in your bot, you need an existing knowledge base in the [QnA Maker](https://www.qnamaker.ai/) portal. +Your bot then can use the knowledge base to answer the user's questions. + +For new bot development, consider using [Copilot Studio](/microsoft-copilot-studio/fundamentals-what-is-copilot-studio). +If you need to create a new knowledge base for a Bot Framework SDK bot, see the following Azure AI services articles: + +- [What is question answering?](/azure/ai-services/language-service/question-answering/overview) +- [Create an FAQ bot](/azure/ai-services/language-service/question-answering/tutorials/bot-service) +- [Azure Cognitive Language Services Question Answering client library for .NET](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/cognitivelanguage/Azure.AI.Language.QuestionAnswering#readme) + +## [C#](#tab/cs) + +:::image type="content" source="./media/tutorial-orchestrator/class-diagram-cs.png" alt-text="C# class diagram."::: + +`OnMessageActivityAsync` is called for each user input received. This module finds the top scoring user intent and passes that result on to `DispatchToTopIntentAsync`. DispatchToTopIntentAsync, in turn, calls the appropriate app handler. + +- `ProcessSampleQnAAsync` - for bot FAQ questions. +- `ProcessWeatherAsync` - for weather queries. +- `ProcessHomeAutomationAsync` - for home lighting commands. + +## [JavaScript](#tab/js) + +:::image type="content" source="./media/tutorial-orchestrator/class-diagram-js.png" alt-text="JavaScript class diagram."::: + +`onMessage` is called for each user input received. This module finds the top scoring user intent and passes that result on to `dispatchToTopIntentAsync`. `dispatchToTopIntentAsync`, in turn, calls the appropriate app handler + +- `processSampleQnA` - for bot FAQ questions. +- `processWeather` - for weather queries. +- `processHomeAutomation` - for home lighting commands. + +--- + +The handler calls the LUIS or QnA Maker service and returns the generated result back to the user. + +## Create LUIS apps + +Before you can create an Orchestrator snapshot file, you need LUIS apps and QnA knowledge bases created and published. The sample bot referenced in this article uses the following models, included with the _NLP With Orchestrator_ sample in the `\CognitiveModels` folder: + +| Name | Description | +|------|------| +| HomeAutomation | A LUIS app that recognizes a home automation intent with associated entity data.| +| Weather | A LUIS app that recognizes weather-related intents with location data.| +| QnAMaker | A QnA Maker knowledge base that provides answers to simple questions about the bot. | + +### Create the LUIS apps + +Create LUIS apps from the _HomeAutomation_ and _Weather_ .lu files in the _cognitive models_ directory of the sample. + +1. Run the following command to import, train and publish the app to the production environment. + + ```console + bf luis:build --in CognitiveModels --authoringKey --botName + ``` + +1. Record the application IDs, display names, authoring key, and location. + +For more information, see how to **Create a LUIS app in the LUIS portal** and **Obtain values to connect to your LUIS app** in [Add natural language understanding to your bot](bot-builder-howto-v4-luis.md) and the LUIS documentation on how to [train](/azure/ai-services/LUIS/how-to/train-test) and [publish](/azure/ai-services/LUIS/how-to/publish) an app to the production environment. + +## Obtain values to connect your bot to the knowledge base + +[!INCLUDE [qnamaker-sunset-alert](../includes/qnamaker-sunset-alert.md)] + +You need an existing knowledge base and your QnA Maker hostname and endpoint key. + +> [!TIP] +> The QnA Maker documentation has instructions on how to [create, train, and publish your knowledge base](/azure/ai-services/qnamaker/quickstarts/create-publish-knowledge-base). + +## Create the Orchestrator snapshot file + +The CLI interface for the Orchestrator tool creates the Orchestrator snapshot file for routing to the correct LUIS or QnA Maker app at run time. + +1. Install the latest supported version of the [Visual C++ Redistributable package](https://support.microsoft.com/help/2977003/the-latest-supported-visual-c-downloads) +1. Open a command prompt or terminal window, and change directories to the sample directory +1. Make sure you have the current version of npm and the Bot Framework CLI. + + ```console + npm i -g npm + npm i -g @microsoft/botframework-cli + ``` + +1. Download Orchestrator base model file + + ```console + mkdir model + bf orchestrator:basemodel:get --out ./model + ``` + +1. Create the Orchestrator snapshot file + + ```console + mkdir generated + bf orchestrator:create --hierarchical --in ./CognitiveModels --out ./generated --model ./model + ``` + +## [C#](#tab/cs) + +### Installing packages + +Prior to running this app for the first time ensure that several NuGet packages are installed: + +- **Microsoft.Bot.Builder** +- **Microsoft.Bot.Builder.AI.Luis** +- **Microsoft.Bot.Builder.AI.QnA** +- **Microsoft.Bot.Builder.AI.Orchestrator** + +### Manually update your appsettings.json file + +Once all of your service apps are created, the information for each needs to be added into your 'appsettings.json' file. The initial sample for [**C#** (archived)][] code contains an empty appsettings.json file: + +**appsettings.json** + +[**C#** (archived)][] + +For each of the entities shown below, add the values you recorded earlier in these instructions: + +```json +"QnAKnowledgebaseId": "", +"QnAEndpointKey": "", +"QnAEndpointHostName": "", + +"LuisHomeAutomationAppId": "", +"LuisWeatherAppId": "", +"LuisAPIKey": "", +"LuisAPIHostName": "", +``` + +When all changes are complete, save this file. + +## [JavaScript](#tab/js) + +### Installing packages + +Prior to running this app for the first time, install required npm packages. + +```console +npm install +``` + +### Manually update your .env file + +Once all of your service apps are created, the information for each needs to be added into your '.env' file. The initial sample for [**JavaScript** (archived)][] code contains an empty .env file. + +**.env** + +[**JavaScript** (archived)][] + +Add your service connection values as shown below: + +```ini +QnAKnowledgebaseId="" +QnAEndpointKey="" +QnAEndpointHostName="" + +WeatherLuisAppId= +HomeAutomationLuisAppId= +LuisAPIKey= +LuisAPIHostName= +``` + +When all changes are in place, save this file. + +--- + +### Connect to the services from your bot + +To connect to the LUIS, and QnA Maker services, your bot pulls information from the settings file. + +## [C#](#tab/cs) + +In **BotServices.cs**, the information contained within configuration file _appsettings.json_ is used to connect your Orchestrator bot to the `HomeAutomation`, `Weather` and `SampleQnA` services. The constructors use the values you provided to connect to these services. + +**BotServices.cs** + +[**C#** (archived)][] + +## [JavaScript](#tab/js) + +In **dispatchBot.js** the information contained within configuration file _.env_ is used to connect your dispatch bot to the _LuisRecognizer(HomeAutomation/Weather)_ and _QnAMaker_ services. The constructors use the values you provided to connect to these services. + +**bots/dispatchBot.js** + +[**JavaScript** (archived)][] + +--- + +### Call the services from your bot + +For each input from your user, the bot logic passes in user input to Orchestrator Recognizer, finds the top returned intent, and uses that information to call the appropriate service for the input. + +## [C#](#tab/cs) + +In the **DispatchBot.cs** file whenever the `OnMessageActivityAsync` method is called, we check the incoming user message and get the top intent from Orchestrator Recognizer. We then pass the `topIntent` and `recognizerResult` on to the correct method to call the service and return the result. + +**bots\DispatchBot.cs** + +[**C#** (archived)][] + +## [JavaScript](#tab/js) + +In the **dispatchBot.js** `onMessage` method, we check the incoming user message and get the top intent from Orchestrator Recognizer. We then pass this on by calling _dispatchToTopIntentAsync_. + +[**JavaScript** (archived)][] + +--- + +### Work with the recognition results + +## [C#](#tab/cs) + +When the Orchestrator recognizer produces a result, it indicates which service can most appropriately process the utterance. The code in this bot routes the request to the corresponding service, and then summarizes the response from the called service. Depending on the _intent_ returned from Orchestrator, this code uses the returned intent to route to the correct LUIS model or QnA service. + +**bots\DispatchBot.cs** + +[**C#** (archived)][] + +The `ProcessHomeAutomationAsync` and `ProcessWeatherAsync` methods use the user input contained within the turn context to get the top intent and entities from the correct LUIS model. + +The `ProcessSampleQnAAsync` method uses the user input contained within the turn context to generate an answer from the knowledge base and display that result to the user. + +## [JavaScript](#tab/js) + +When the Orchestrator recognizer produces a result, it indicates which service can most appropriately process the utterance. The code in this sample uses the recognized _topIntent_ to show how to route the request on to the corresponding service. + +**bots/dispatchBot.js** + +[**JavaScript** (archived)][] + +The `processHomeAutomation` and `processWeather` methods use the user input contained within the turn context to get the top intent and entities from the correct LUIS model. + +The `processSampleQnA` method uses the user input contained within the turn context to generate an answer from the knowledge base and display that result to the user. + +--- + +> [!NOTE] +> If this were a production application, this is where the selected LUIS methods would connect to their specified service, pass in the user input, and process the returned LUIS intent and entity data. + +## Test your bot + +1. Using your development environment, start the sample code. Note the _localhost_ address shown in the address bar of the browser window opened by your App: `https://localhost:`. +1. Open Bot Framework Emulator, click on **Open Bot** button. +1. In the **Open a bot** dialog box, enter your bot endpoint URL, such as `http://localhost:3978/api/messages`. Click **Connect**. +1. For your reference, here are some of the questions and commands that are covered by the services built for your bot: + + - QnA Maker + - `hi`, `good morning` + - `what are you`, `what do you do` + - LUIS (home automation) + - `turn on bedroom light` + - `turn off bedroom light` + - `make some coffee` + - LUIS (weather) + - `whats the weather in redmond washington` + - `what's the forecast for london` + - `show me the forecast for nebraska` + +## Route user utterance to QnA Maker + +1. In the Emulator, enter the text `hi` and submit the utterance. The bot submits this query to Orchestrator and gets back a response indicating which child app should get this utterance for further processing. + +1. By selecting the `Orchestrator Recognition Trace` line in the log, you can see the JSON response in the Emulator. The Orchestrator result is displayed in the Inspector. + + ```json + { + "type": "trace", + "timestamp": "2021-05-01T06:26:04.067Z", + "serviceUrl": "http://localhost:58895", + "channelId": "emulator", + "from": { + "id": "36b2a460-aa43-11eb-920f-7da472b36492", + "name": "Bot", + "role": "bot" + }, + "conversation": { + "id": "17ef3f40-aa46-11eb-920f-7da472b36492|livechat" + }, + "recipient": { + "id": "5f8c6123-2596-45df-928c-566d44426556", + "role": "user" + }, + "locale": "en-US", + "replyToId": "1a3f70d0-aa46-11eb-8b97-2b2a779de581", + "label": "Orchestrator Recognition", + "valueType": "OrchestratorRecognizer", + "value": { + "text": "hi", + "alteredText": null, + "intents": { + "QnAMaker": { + "score": 0.9987310956576168 + }, + "HomeAutomation": { + "score": 0.3402091165577196 + }, + "Weather": { + "score": 0.24092200496795158 + } + }, + "entities": {}, + "result": [ + { + "Label": { + "Type": 1, + "Name": "QnAMaker", + "Span": { + "Offset": 0, + "Length": 2 + } + }, + "Score": 0.9987310956576168, + "ClosestText": "hi" + }, + { + "Label": { + "Type": 1, + "Name": "HomeAutomation", + "Span": { + "Offset": 0, + "Length": 2 + } + }, + "Score": 0.3402091165577196, + "ClosestText": "make some coffee" + }, + { + "Label": { + "Type": 1, + "Name": "Weather", + "Span": { + "Offset": 0, + "Length": 2 + } + }, + "Score": 0.24092200496795158, + "ClosestText": "soliciting today's weather" + } + ] + }, + "name": "OrchestratorRecognizerResult", + "id": "1ae65f30-aa46-11eb-8b97-2b2a779de581", + "localTimestamp": "2021-04-30T23:26:04-07:00" + } + ``` + + Because the utterance, `hi`, is part of the Orchestrator's **QnAMaker** intent, and is selected as the `topScoringIntent`, the bot will make a second request, this time to the QnA Maker app, with the same utterance. + +1. Select the `QnAMaker Trace` line in the Emulator log. The QnA Maker result displays in the Inspector. + + ```json + { + "questions": [ + "hi", + "greetings", + "good morning", + "good evening" + ], + "answer": "Hello!", + "score": 1, + "id": 96, + "source": "QnAMaker.tsv", + "metadata": [], + "context": { + "isContextOnly": false, + "prompts": [] + } + } + ``` + +[howto-luis]: bot-builder-howto-v4-luis.md +[howto-qna]: bot-builder-howto-qna.md + +[**C#** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/csharp_dotnetcore/14.nlp-with-orchestrator +[**JavaScript** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/javascript_nodejs/14.nlp-with-orchestrator +[**Java** (archived)]: https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/java_springboot/14.nlp-with-orchestrator +[**Python** (archived)]: (https://github.com/microsoft/BotBuilder-Samples/tree/main/archive/samples/python/14.nlp-with-orchestrator + +[orchestrator]: /composer/concept-orchestrator +[bf-cli]: https://github.com/microsoft/botframework-cli +[bf-orchestrator-cli]: https://github.com/microsoft/botframework-cli/tree/main/packages/orchestrator#readme diff --git a/articles/v4sdk/bot-builder-virtual-assistant-introduction.md b/articles/v4sdk/bot-builder-virtual-assistant-introduction.md index ffb0eba30..6c9742624 100644 --- a/articles/v4sdk/bot-builder-virtual-assistant-introduction.md +++ b/articles/v4sdk/bot-builder-virtual-assistant-introduction.md @@ -1,105 +1,25 @@ --- -title: Virtual Assistant Overview - Bot Service -description: Learn about creating your own Virtual Assistant -author: darrenj -ms.author: darrenj -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' +title: Virtual Assistant overview - Bot Service +description: Become familiar with the Bot Framework Virtual Assistant template. Learn about features, understand design principles, and view example scenarios. +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: overview +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- -# Virtual Assistant Overview +# Virtual Assistant overview -## Overview +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] -Customers and partners have a significant need to deliver a conversational assistant tailored to their brand, personalized to their users, and made available across a broad range of canvases and devices. +Continuing Microsoft's open-sourced approach towards the Bot Framework SDK, the open-source Virtual Assistant solution provides a set of core foundational capabilities and full control over the end user experience and data. -Continuing Microsoft's open-sourced approach towards the Bot Framework SDK, the open-source Virtual Assistant solution provides you with a set of core foundational capabilities and full control over the end user experience. +At its core the Virtual Assistant (available in C# and TypeScript) is a project template with the best practices for developing a bot on the Microsoft Azure platform. -This template incorporates the previous Enterprise Template and brings together all of the best practices and supporting components identified through building conversational experiences and greatly simplifies the creation of a new bot project including: basic conversational intents, Dispatch integration, QnA Maker, Application Insights and an automated deployment. - -We strongly believe our customers should own and enrich their customer relationships and insights. Therefore, any Virtual Assistant provides complete control of the user experience to our customers and partners through open-sourcing the code on GitHub. The name, voice and personality can be changed to suit the organization’s needs. Our Virtual Assistant solution simplifies creation of your own assistant enabling you to get started in minutes and then extended using our end to end development tooling. - -The scope of Virtual Assistant functionality is broad, typically offering end users a range of capabilities. To increase developer productivity and to enable a vibrant ecosystem of reusable conversational experiences, we are providing developers initial examples of reusable conversational skills. These Skills can be added into a conversational application to lighten up a specific conversation experience, such as finding a point of interest, interacting with calendar, tasks, email and many other scenarios. Skills are fully customizable and consist of language models for multiple languages, dialogs and code. - -![Virtual Assistant Diagram](./media/enterprise-template/customassistantdiagram.jpg) - -## Getting Started - -Explore the [Virtual Assistant and Skills](https://aka.ms/bf-solutions-docs) documentation for more detailed information. - -## What's in the box - -The Virtual Assistant Template brings together a number of best practices we've identified through the building of conversational experiences and automates integration of components that we've found to be highly beneficial to Bot Framework developers. This section covers some background to key decisions to help explain why the template works the way it does. - -The Virtual Assistant template now incorporates the previous Enterprise Template capabilities including base conversational intents in multiple languages, Dispatching, QnA, and conversational insights. The following Assistant related capabilities are provided at this time; further capabilities are planned and we'll be working closely with customers and partners to help inform the roadmap. - -Feature | Description | ------------- | ------------- -Onboarding | An example OnBoarding flow enabling your Assistant to greet the user and collect initial information. -Eventing Architecture | Events in the context of the Virtual Assistant enable the client application hosting the assistant (in a web-browser or on a device such as a car or speaker) to exchange information about the user or device events while also receiving events to perform device operations. -Linked Accounts | In a speech-led scenario it's not practical for a user to enter their username and password for supporting systems through voice commands. Therefore, a separate companion experience provides an opportunity for the user to signin and provide permission for an Virtual Assistant to retrieve tokens for later use. -Skill Enablement | A broad set of common capabilities exist today, which require each developer to build themselves. Our Virtual Assistant solution includes a new Skill capability enabling new capabilities to be plugged into an Virtual Assistant through configuration only and provide an authentication mechanism for Skills to request tokens for down-stream activities. -Point of Interest Skill | The preview Point of Interest (PoI) skill provides a comprehensive language model for finding points of interest and requesting directions. The skill currently provides integration into Azure Maps. -Calendar Skill | The preview Calendar Skill provides a comprehensive language model for common calendar related activities, The skill is currently integrated into Microsoft Graph (Office 365/Outlook.com) with support for Google APIs to follow soon. -Email Skill | The preview Email Skill provides a comprehensive language model for common email related activities, The skill is currently integrated into Microsoft Graph (Office 365/Outlook.com) with support for Google APIs to follow soon. -To Do Skill | The preview To Do Skill provides a comprehensive language model for common task related activities, The skill is currently integrated into OneNote with Microsoft Graph (outlookTask) support to follow soon. -Device Integration | Our Azure Bot Service SDKs (DirectLine) along with Adaptive Card and Speech SDKs enable easy cross-platform integration to devices. Additional device integration examples and platform including Edge are planned. -Test Harnesses | In addition to the Bot Framework Emulator, a WebChat based test harness is provided enabling more complex authentication scenarios to be tested. A simple Console based test harness demonstrates the approach to exchange messages to help frame the ease of device integration. -Automated Deployment | All the Azure resources required for your Assistant are automatically deployed: Bot registration, Azure App Service, LUIS, QnAMaker, Content Moderator, CosmosDB, Azure Storage, and Application Insights. Additionally, LUIS models for all skills, QnAMaker, and Dispatch models are created, trained, and published to enable immediate testing. -Automotive Language Model | An Automotive language model covering core domains such as telephone, navigation and control of in-car features is coming soon. - -## Example Scenarios - -The Virtual Assistant extends across a broad number of industry scenarios. Some example scenarios are shown below for reference purposes. - -- **Automotive Industry**: Voice enabled Personal Assistant integrated into the car providing end users the ability to perform traditional car operations (e.g. navigation, radio) along with productivity focused scenarios such as moving meetings when you're running late, adding items to your task list and proactive experiences where the car can suggest tasks to complete based on events such as starting the engine, traveling home or enabling cruise control. Adaptive Cards are rendered within the Head Unit and speech integration performed through Push-To-Talk or Wake Word interactions. - -- **Hospitality**: Voice enabled Personal Assistant integrated into a hotel room device providing a broad range of hospitality focused scenarios (e.g. extend your stay, request late checkout, room service) including concierge and the ability to find local restaurants and attractions. Optional linking to your productivity accounts open up more personalized experiences such as suggested alarm calls, weather warnings and learning of patterns across stays. An evolution of the current TV personalization experienced in room today. - -- **Enterprise**: Voice and Text enabled branded Employee Assistant experiences integrated into enterprise devices and existing conversation canvases (e.g. Teams, WebChat, Slack) enabling employees to manage their calendars, find available meeting rooms, find people with specific skills, or perform HR related operations. - -## Virtual Assistant Principles - -### Your data, your brand and your experience -All aspects of the end user experience are owned and controlled by you. This includes the branding, name, voice, personality, responses, and avatar. The source code to the Virtual Assistant and supporting Skills are provided in full, enabling you to adjust as required. - -Your Virtual Assistant will be deployed within your Azure subscription. Therefore, all data generated by your assistant (questions asked, user behaviour, etc.) is entirely contained within your Azure subscription. See [Cognitive Services Azure Trusted Cloud](https://www.microsoft.com/trustcenter/cloudservices/cognitiveservices), and more specifically the [Azure section of the Trust Center](https://www.microsoft.com/TrustCenter/CloudServices/Azure), for additional information. - -### Write it once, embed it anywhere -The Virtual Assistant leverages the Microsoft Conversational AI platform and therefore can be surfaced through any Bot Framework [channel](https://docs.microsoft.com/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0). - -In addition, you can embed experiences into desktop and mobile apps (like cars, speakers, and alarm clocks) through the [Direct Line](https://docs.microsoft.com/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-concepts?view=azure-bot-service-4.0) channel. - -### Enterprise Grade Solutions -The Virtual Assistant solution is built on the Azure Bot Service, Language Understanding Cognitive Service, and Unified Speech along with a broad set of supporting Azure components. This means that you benefit from the [Azure global infrastructure](https://azure.microsoft.com/global-infrastructure/), including ISO 27018, HIPPA, PCI DSS, and SOC 1, 2, and 3 certification. - -In addition, Language Understanding support is provided by the LUIS Cognitive Service which supports a broad set of languages [listed here](https://docs.microsoft.com/azure/cognitive-services/luis/luis-supported-languages). The [Translator Cognitive Service](https://azure.microsoft.com/services/cognitive-services/translator-text-api/) provides additional machine translation capabilities to extend the reach of your Virtual Assistant even further. - -### Integrated and Context Aware -Your Virtual Assistant can be incoroporated into your device and ecosystem, enabling a truly integrated and intelligent experience. Through this contextual awareness more intelligent experiences can be developed and deliver further personalization than otherwise possible. - -### 3rd Party assistant integration -The Virtual Assistant enables you to deliver your own unique experience but also hand-off to the end-users chosen Digital Assistant for certain types of questions. - -### Flexible integration -Our Virtual Assistant architecture is flexible and can be integrated with existing investments you may have made into device-based speech or natural language processing capabilities that integrate with your existing back-end systems and APIs. - -### Adaptive Cards -[Adaptive Cards](https://adaptivecards.io/) provide the ability for your Virtual Assistant to return user experience (UX) elements (e.g. Cards, Images, Buttons) alongside text-based responses. If the device or conversation canvas has a screen these Adaptive Cards can be rendered across a broad range of devices and platforms, providing UX support where appropriate. Examples of Adaptive Cards can be found [here](https://adaptivecards.io/samples/) with information on rendering options in the documentation [here](https://docs.microsoft.com/adaptive-cards/rendering-cards/getting-started). - -### Skills -In addition to the base assistant there exists a broad set of common capabilities which require each developer to build themselves. Productivity is a great example where each organization would need to create language models (LUIS), dialogs (code), integration (code) and language generation (responses) to enable popular Calendar, Task or email experiences. - -This is then further complicated by the need to support multiple languages and results in a large amount of work required for any organisation building their own assistant. - -Our Virtual Assistant solution includes a new Skill capability that enables capabilities to be plugged into a custom-assistant through configuration only. - -All aspects of each Skill (language model, dialogs, integration code and language generation) are completely customizable by developers as the full source code is provided on GitHub along with the Virtual Assistant. - -## Getting Started - -Refer to the [tutorials](https://aka.ms/bfs-tutorials) to learn how to create and deploy your Virtual Assistant. +- The Virtual Assistant template brings together many best practices identified through the building of conversational experiences and automates integration of components found to be highly beneficial to Bot Framework developers. +- Bot Framework skills are re-usable conversational skill building-blocks covering conversational use-cases enabling you to add extensive functionality to a bot within minutes. Virtual Assistant provides a number of skills. +Read more about Virtual Assistant in the [Bot Framework Solutions Documentation](https://microsoft.github.io/botframework-solutions/index). diff --git a/articles/v4sdk/bot-builder-virtual-assistant-template.md b/articles/v4sdk/bot-builder-virtual-assistant-template.md deleted file mode 100644 index 412cd46b4..000000000 --- a/articles/v4sdk/bot-builder-virtual-assistant-template.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Overview of the Virtual Assistant Template - Bot Service -description: Learn more about the Virtual Assistant Template -author: darrenj -ms.author: darrenj -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 05/23/2019 -monikerRange: 'azure-bot-service-4.0' ---- - -# Virtual Assistant - Template Outline - -> [!NOTE] -> This topic applies to v4 version of the SDK. - -The Virtual Assistant Template brings together a number of best practices we've identified through the building of conversational experiences and automates integration of components that we've found to be highly beneficial to Bot Framework developers. This section covers some background to key decisions to help explain why the template works the way it does. - -Feature | Description | ------------- | ------------- -Introduction | Introduction message with an [Adaptive Card]() on conversation start -Typing indicators | Automated visual typing indicators during conversations and repeat for long running operations -Base LUIS model | Supports common intents such as **Cancel**, **Help**, **Escalate**, etc. -Base dialogs | Dialog flows for capturing basic user information as well as interruption logic for cancel and help intents -Base responses | Text and speech responses for base intents and dialogs -FAQ | Integration with [QnA Maker](https://www.qnamaker.ai) to answer general questions from a knowledgebase -Chit-chat | A professional chit-chat model to provide standard answers to common queries ([learn more](https://docs.microsoft.com/azure/cognitive-services/qnamaker/how-to/chit-chat-knowledge-base)) -Dispatcher | An integrated [Dispatch](https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-dispatch?view=azure-bot-service-4.0&tabs=csaddref%2Ccsbotconfig) model to identify whether a given utterance should be processed by LUIS or QnA Maker. -Language support | Available in English, French, Italian, German, Spanish and Chinese -Transcripts | Transcripts of all conversations stored in Azure Storage -Telemetry | [Application Insights](https://azure.microsoft.com/services/application-insights/) integration to collect telemetry for all conversations -Analytics | An example Power BI dashboard to get you started with insights into your conversational experiences. -Automated deployment | Easy deployment of all aforementioned services using Azure ARM templates. - -## Introduction Card - -A key issue with many conversational experiences is end-users not knowing how to get started, leading to general questions that the Bot may not be best placed to answer. First impressions matter! An introduction card offers an opportunity to introduce the Bot's capabilities to an end user and suggests a few initial questions the user can use to get started. It's also a great opportunity to surface the personality of your Bot. - -A simple introduction card is provided as standard which you can adapt as needed, a returning user card is shown on subsequent interactions when a user has completed the onboarding dialog (triggered by the Get Started button on the Introduction card) - -![Intro Card Example](./media/enterprise-template/vabotintrocard.png) - -## Basic Language Understanding (LUIS) intents - -Every Bot should handle a base level of conversational language understanding. Greetings for example are a basic thing every Bot should handle with ease. Typically, developers need to create these base intents and provide initial training data to get started. The Virtual Assistant template provides example .lu files to get you started and avoids every project having to create these each time and ensures a base level of capability out of the box. - -The .lu files provide the following intents across English, Chinese, French, Italian, German, Spanish. - -Intent | Sample Utterances | --------------|-------------| -Cancel |*cancel*, *nevermind*| -Escalate |*can I talk to a person?*| -FinishTask |*done*, *all finished*| -GoBack |*go back*| -Help |*can you help me?*| -Repeat |*can you say that again?*| -SelectAny |*any of these*| -SelectItem |*the first one*| -SelectNone |*none of these*| -ShowNext |*show more*| -ShowPrevious |*show previous*| -StartOver |*restart*| -Stop |*stop*| - -The [.lu](https://github.com/Microsoft/botbuilder-tools/blob/master/packages/Ludown/docs/lu-file-format.md) format is similar to Markdown, enabling easy modification and source control. The [LuDown](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/Ludown) tool is then used to convert .lu files into LUIS models which can then be published to your LUIS subscription either through the portal or the associated [LUIS](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/LUIS) CLI (command line) tool. - -## Telemetry - -Providing insights into the user engagement of your Bot has proven to be highly valuable. This insight can help you understand the levels of user engagement, what features of the Bot they are using (intents) along with questions people are asking that the Bot isn't able to answer - highlighting gaps in the Bot's knowledge that could be addressed through new QnA Maker articles for instance. - -Integration of Application Insights provides significant operational/technical insight out of the box but this can also be used to capture specific Bot related events - messages sent and received along with LUIS and QnA Maker operations. - -Bot level telemetry is intrinsically linked to technical and operational telemetry enabling you to inspect how a given user question was answered and vice versa. - -A middleware component combined with a wrapper class around the QnA Maker and LuisRecognizer SDK classes provides an elegant way to collect a consistent set of events. These consistent events can then be used by the Application Insights tooling along with tools like PowerBI. - -An example Power BI dashboard is as part of the Bot Framework Solutions github repo and works right out of the box with every Virtual Assistant template. See the [Analytics](https://aka.ms/bfs-analytics) section for more information. - -![Analytics Example](./media/enterprise-template/powerbi-conversationanalytics-luisintents.png) - -## Dispatcher - -A key design pattern used to good effect in the first wave of conversational experiences was to leverage Language Understanding (LUIS) and QnA Maker. LUIS would be trained with tasks that your Bot could do for an end user and QnA Maker would be trained with more general knowledge. - -All incoming utterances (questions) would be routed to LUIS for analysis. If the intent of a given utterance was not identified it was marked as a *None* intent. QnA Maker was then used to try and find an answer for the end-user. - -While this pattern worked well there were two key scenarios where problems could be experienced. - -- If utterances in the LUIS model and QnA Maker overlapped, sometimes slightly, this could lead to strange behavior where LUIS may try to process a question when it should have been directed to QnA Maker. -- When there were two or more LUIS models a Bot would have to invoke each one and perform some form of intent evaluation comparison to identify where to send a given utterance. As there is no common baseline score, comparison across models didn't work effectively, leading to a poor user experience. - -[Dispatch](https://docs.microsoft.com/azure/bot-service/bot-builder-tutorial-dispatch?view=azure-bot-service-4.0&tabs=csaddref%2Ccsbotconfig) provides an elegant solution to this by extracting utterances from each configured LUIS model and questions from QnA Maker and creating a central dispatch LUIS model. - -This enables a Bot to quickly identify which LUIS model or component should handle a given utterance and ensures QnA Maker data is considered at the top level of intent processing, not just the *None* intent as before. - -This Dispatch tool also enables evaluation which will highlight confusion, issues, and overlap across LUIS models and QnA Maker knowledge bases before deployment. - -The Dispatcher is used at the core of each project created using the template. The Dispatch model is used within the `MainDialog` class to identify whether the target is a LUIS model or QnA. In the case of LUIS, the secondary LUIS model is invoked returning the intent and entities. Dispatcher is also used for interruption detection. - -![Dispatch Example](./media/enterprise-template/dispatchexample.png) - -## QnA Maker - -[QnA Maker](https://www.qnamaker.ai/) provides the ability for non-developers to curate general knowledge in the format of question and answer pairs. This knowledge can be imported from FAQ data sources, product manuals and interactively within the QnA Maker portal. - -Two example QnA Maker models are provided in the [.lu](https://github.com/Microsoft/botbuilder-tools/blob/master/packages/Ludown/docs/lu-file-format.md) file format within the QnA folder of CognitiveModels, one for FAQ and one for chit-chat. [LUDown](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/Ludown) is then used as part of the deployment script to create a QnA Maker JSON file which the [QnA Maker](https://github.com/Microsoft/botbuilder-tools/tree/master/packages/QnAMaker) CLI (command line) tool then uses to publish items to the QnA Maker knowledge base. - -![QnA ChitChat example](./media/enterprise-template/qnachitchatexample.png) - -## Content Moderator - -Content Moderator is an optional component which enables detection of potential profanity and helps check for personally identifiable information (PII). For example, a Bot can apologise and hand-off to a human in the event of profanity, or not store telemetry records if PII information is detected. - -A middleware component is provided that screens text and surfaces through a ```TextModeratorResult``` on the TurnState object. - -## Next Steps -Refer to the [tutorials](https://aka.ms/bfs-tutorials) to learn how to create and deploy your Virtual Assistant. - -## Additional resources -Full source code for the Virtual Assistant Template can be found on [GitHub](https://aka.ms/bf-solutions). - diff --git a/articles/v4sdk/bot-builder-webchat-customization.md b/articles/v4sdk/bot-builder-webchat-customization.md index b4ad007d5..0e55ac9c8 100644 --- a/articles/v4sdk/bot-builder-webchat-customization.md +++ b/articles/v4sdk/bot-builder-webchat-customization.md @@ -1,208 +1,195 @@ --- -title: Web Chat customization - Bot Service -description: Learn how to customize the Bot Framework Web Chat. +title: Web Chat customization in the Bot Framework SDK +description: Learn how to customize the Web Chat control. Add attachments, modify the font, color, container size, and bot avatar. keywords: bot framework, webchat, chat, samples, react, reference -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 06/07/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.topic: how-to +ms.service: azure-ai-bot-service +ms.custom: + - evergreen --- # Web Chat customization +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + This article details how to customize the Web Chat samples to fit your bot. ## Integrate Web Chat into your website -Follow the instructions on the [overview page](bot-builder-webchat-overview.md) page to integrate the Web Chat control into your website. +The [Web Chat overview](bot-builder-webchat-overview.md) describes how to integrate the Web Chat control into your website. ## Customizing styles -The latest version of Web Chat control provides rich customization options: you can change colors, sizes, placement of elements, add custom elements, and interact with the hosting webpage. Below are several examples of how to customize those elements of the Web Chat UI. +The Web Chat control provides rich customization options: you can change colors, sizes, placement of elements, add custom elements, and interact with the hosting webpage. Below are several examples of how to customize the Web Chat UI. -You can find the full list of all settings that you can easily modify in Web Chat on the [`defaultStyleOptions.js` file](https://github.com/Microsoft/BotFramework-WebChat/blob/master/packages/component/src/Styles/defaultStyleOptions.js). +You can find the full list of all the settings that you can modify in Web Chat in the [`StyleOptions.ts`]( https://github.com/microsoft/BotFramework-WebChat/blob/master/packages/api/src/StyleOptions.ts) file. The default values for Web Chat can be found in the [defaultStyleOptions.ts](https://github.com/microsoft/BotFramework-WebChat/blob/master/packages/api/src/defaultStyleOptions.ts) file -These settings will generate a _style set_, which is a set of CSS rules enhanced with [glamor](https://github.com/threepointone/glamor). You can find the full list of CSS styles generated in the style set on the [`createStyleSet.js` file](https://github.com/Microsoft/BotFramework-WebChat/blob/master/packages/component/src/Styles/createStyleSet.js). +These settings will generate a _style set_, which is a set of CSS rules enhanced with [glamor](https://github.com/threepointone/glamor). You can find the full list of CSS styles generated in the style set in the [`createStyleSet.ts`](https://github.com/Microsoft/BotFramework-WebChat/blob/master/packages/component/src/Styles/createStyleSet.ts) file. ## Set the size of the Web Chat container -It is now possible to adjust the size of the Web Chat container using `styleSetOptions`. The following example has a `body` background-color of `paleturquoise` to show the Web Chat container (section with white background). +To adjust the size of the Web Chat container, use the _style set's_ `rootHeight` and `rootWidth` properties. The following example also sets the container's background color to show the size of the controller. -```js -… +```html + - + -
+
-… + ``` -Here is the result: +:::image type="content" source="../media/bot-service-channel-webchat/chat-container-height-width.png" alt-text="Set Web Chat with root height and root width"::: -Web Chat with root height and root width set +> [!WARNING] +> In the hosting webpage, don't use your Direct Line key in plain sight. Use a token as explained in the [Production embedding option](../bot-service-channel-connect-webchat.md#production-embedding-option) section of how to **Connect a bot to Web Chat**. -## Change font or color +## Change chat bubble font and color -Instead of using the default background color and the fonts used inside of the chat bubbles, you can customize those to match the style of the target web page. The code snippet below allows you to change the background color of messages from the user and from the bot. +You can customize the background color and the fonts used in the chat bubbles to match the style of the webpage hosting the Web Chat control. The code snippet below shows how to do it. -Screenshot with custom style options - -If you need to do some simple styling, you can set them via `styleOptions`. Style options are set of predefined styles that you can modify directly, and Web Chat will compute the whole stylesheet based on it. +:::image type="content" source="../media/bot-service-channel-webchat/bubbles-font-color.png" alt-text="Set bubbles font and color"::: ```html - - -
- - - - + + + + +
+ + ``` -## Change the CSS manually - -In addition to colors, you can modify fonts used to render messages: - -Screenshot with custom style set +## Change bot and user avatars -For deeper styling, you can also modify the style set manually by setting the CSS rules directly. +Web Chat supports avatars, which you can customize by setting `botAvatarInitials` and `userAvatarInitials` in the `styleOptions` property. -> Since CSS rules are tightly-coupled to the structure of the DOM tree, there is possibility that these rules need to be updated to work with the newer version of Web Chat. +:::image type="content" source="../media/bot-service-channel-webchat/set-avatar-initials.png" alt-text="set avatar initials"::: ```html - - -
- - + + +
+ - - + window.WebChat.renderWebChat({ + directLine: window.WebChat.createDirectLine({ + token: ''}), + styleSet, + styleOptions: avatarOptions + }, document.getElementById('webchat')); + + + ``` -## Change the avatar of the bot within the dialog box - -The latest version of Web Chat supports avatars, which you can customize by setting `botAvatarInitials` and `userAvatarInitials` in the `styleOptions` prop. +Use the `botAvatarInitials` property to set the avatar initials for the bot, which appears on the left-hand side of the control. +Use the `userAvatarInitials` property to set the avatar initials for the user, which appear on the right-hand side. -Screenshot with avatar initials - -```html - - - -
- - - - -``` - -Inside Web Chat's `styleOptions` prop, we added `botAvatarInitials` and `userAvatarInitials`: +Use the `botAvatarImage` and `userAvatarImage` properties to provide image URLs for the bot and user avatars. The control will display these in place of the initials, as shown below. ```js -botAvatarInitials: 'BF', -userAvatarInitials: 'WC' +const avatarOptions = { + botAvatarImage: '', + botAvatarInitials: 'BF', + userAvatarImage: '', + userAvatarInitials: 'WC' +}; ``` -`botAvatarInitials` will set the text inside the avatar on the left-hand side. If it is set to falsy value, the avatar on the bot side will be hidden. In contrast, `userAvatarInitials` will set the avatar text on the right-hand side. +:::image type="content" source="../media/bot-service-channel-webchat/set-avatar-custom.png" alt-text="set avatar custom"::: ## Custom rendering activity or attachment -With the latest version of Web Chat, you can also render activities or attachments that Web Chat does not support out-of-the-box. Activities and attachments render are sent thru a customizable pipeline that modeled after [Redux middleware](https://redux.js.org/api/applymiddleware). The pipeline is flexible enough that you can do the following tasks easily: +With the latest version of Web Chat, you can also render activities or attachments that Web Chat doesn't support out-of-the-box. Activities and attachments render are sent thru a customizable pipeline that modeled after [Redux middleware](https://redux.js.org/api/applymiddleware). The pipeline is flexible enough that you can do the following tasks easily: -- Decorate existing activities/attachments -- Add new activities/attachments -- Replace existing activities/attachments (or remove them) -- Daisy chain middleware together +- Decorate existing activities/attachments +- Add new activities/attachments +- Replace existing activities/attachments (or remove them) +- Daisy chain middleware together ### Show GitHub repository as an attachment -If you want to display a deck of GitHub repository cards, you can create a new React component for the GitHub repository and add it as a middleware for attachment. +For example, if you want to display a deck of GitHub repository cards, you can create a new React component for the GitHub repository and add it as middleware. The following image and code snippets are from the sample [the customization-card-components sample](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/05.custom-components/e.card-components). + +The following is the output when you enter the default message: *sample:github-repository*. + +:::image type="content" source="../media/bot-service-channel-webchat/github-repo-attachments-custom.png" alt-text="github repo attachments custom"::: -Screenshot with custom GitHub repository attachment +If you enter *help* you obtain the selection of all the cards you can choose. This is one of many examples: + +:::image type="content" source="../media/bot-service-channel-webchat/image-attachment-custom.png" alt-text="image attachment custom"::: ```jsx import ReactWebChat from 'botframework-webchat'; @@ -269,8 +256,6 @@ ReactDOM.render( ); ``` -The full sample can be found in [the customization-card-components sample](https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/10.a.customization-card-components). - In this sample, we are adding a new React component called `GitHubRepositoryAttachment`: ```jsx diff --git a/articles/v4sdk/bot-builder-webchat-overview.md b/articles/v4sdk/bot-builder-webchat-overview.md index def59c5c9..e9daa9a76 100644 --- a/articles/v4sdk/bot-builder-webchat-overview.md +++ b/articles/v4sdk/bot-builder-webchat-overview.md @@ -1,30 +1,34 @@ --- title: Web Chat overview - Bot Service -description: Learn how to configure Bot Framework Web Chat. +description: Become familiar with the Bot Framework Web Chat component. Learn how to use and customize this component. View available properties and other information. keywords: bot framework, webchat, chat, samples, react, reference -author: ivorb -ms.author: kamrani -manager: kamrani -ms.topic: article -ms.service: bot-service -ms.date: 06/07/2019 +author: JonathanFingold +ms.author: iawilt +manager: shellyha +ms.reviewer: micchow +ms.service: azure-ai-bot-service +ms.topic: overview +ms.custom: + - evergreen --- # Web Chat overview +[!INCLUDE [applies-to-v4](../includes/applies-to-v4-current.md)] + This article contains details of the [Bot Framework Web Chat](https://github.com/microsoft/BotFramework-WebChat) component. The Bot Framework Web Chat component is a highly customizable web-based client for the Bot Framework V4 SDK. The Bot Framework SDK v4 enables developers to model conversation and build sophisticated bot applications. If you're looking to migrate from Web Chat v3 to v4, jump ahead to [the migration section](#migrating-from-web-chat-v3-to-v4). -## How to use +## Get started with Web Chat > [!NOTE] > For previous versions of Web Chat (v3), visit the [Web Chat v3 branch](https://github.com/Microsoft/BotFramework-WebChat/tree/v3). -First, create a bot using [Azure Bot Service](https://azure.microsoft.com/services/bot-service/). -Once the bot is created, you will need to [obtain the bot's Web Chat secret](../bot-service-channel-connect-webchat.md#get-your-bot-secret-key) in Azure Portal. Then use the secret to [generate a token](../rest-api/bot-framework-rest-direct-line-3-0-authentication.md) and pass it to your Web Chat. +First, create a bot using [Azure AI Bot Service](https://azure.microsoft.com/services/bot-service/). +Once the bot is created, you'll need to [obtain the bot's Web Chat secret](../bot-service-channel-connect-webchat.md#get-your-bot-secret-key) in Azure portal. Then use the secret to [generate a token](../rest-api/bot-framework-rest-direct-line-3-0-authentication.md) and pass it to your Web Chat. -Here is how how you can add a Web Chat control to your website: +The following example shows how to add a Web Chat control to a website. ```html @@ -33,6 +37,13 @@ Here is how how you can add a Web Chat control to your website: