diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..dabcc8d3a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# EditorConfig is awesome: http://EditorConfig.org
+# File take from the VSCode repo at:
+# https://github.com/Microsoft/vscode/blob/master/.editorconfig
+
+# top-most EditorConfig file
+root = true
+
+# Tab indentation
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.xml]
+indent_size = 2
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..ad136dfa8
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,15 @@
+# Lines starting with '#' are comments.
+# Each line is a file pattern followed by one or more owners.
+
+# More details are here: https://help.github.com/articles/about-codeowners/
+
+# The '*' pattern is global owners.
+
+# Order is important. The last matching pattern has the most precedence.
+# The folders are ordered as follows:
+
+# In each subsection folders are ordered first by depth, then alphabetically.
+# This should make it easy to add new rules without breaking existing ones.
+
+# Global rule:
+* @microsoft/botframework-sdk
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/java-sdk-bug-report.md b/.github/ISSUE_TEMPLATE/java-sdk-bug-report.md
new file mode 100644
index 000000000..f34d7c975
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/java-sdk-bug-report.md
@@ -0,0 +1,31 @@
+---
+name: Java SDK Bug Report
+about: Create a bug report for a bug you found in the Bot Builder Java SDK
+title: ""
+labels: "needs-triage, bug"
+assignees: ""
+---
+
+### [Github issues](https://github.com/microsoft/botbuilder-java/issues) should be used for bugs and feature requests. Use [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) for general "how-to" questions.
+
+## Version
+What package version of the SDK are you using.
+
+## Describe the bug
+Give a clear and concise description of what the bug is.
+
+## To Reproduce
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+## Expected behavior
+Give a clear and concise description of what you expected to happen.
+
+## Screenshots
+If applicable, add screenshots to help explain your problem.
+
+## Additional context
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/java-sdk-feature-request.md b/.github/ISSUE_TEMPLATE/java-sdk-feature-request.md
new file mode 100644
index 000000000..b12c217c2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/java-sdk-feature-request.md
@@ -0,0 +1,21 @@
+---
+name: Java SDK Feature Request
+about: 'Suggest a feature for the Bot Builder Java SDK '
+title: ""
+labels: "needs-triage, feature-request"
+assignees: ""
+---
+
+### Use this [query](https://github.com/microsoft/botbuilder-java/issues?q=is%3Aissue+is%3Aopen++label%3Afeature-request+) to search for the most popular feature requests.
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/java-sdk-question.md b/.github/ISSUE_TEMPLATE/java-sdk-question.md
new file mode 100644
index 000000000..71a5792af
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/java-sdk-question.md
@@ -0,0 +1,11 @@
+---
+name: Java SDK Question
+about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/botframework
+
+---
+
+🚨 The issue tracker is not for questions 🚨
+
+If you have a question, please ask it on https://stackoverflow.com/questions/tagged/botframework
+
+[question]
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..e8870cd7e
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,14 @@
+Fixes #
+
+## Description
+
+
+## Specific Changes
+
+
+ -
+ -
+ -
+
+## Testing
+
\ No newline at end of file
diff --git a/.github/workflows/create-parity-issue.yml b/.github/workflows/create-parity-issue.yml
new file mode 100644
index 000000000..51f47a190
--- /dev/null
+++ b/.github/workflows/create-parity-issue.yml
@@ -0,0 +1,43 @@
+name: create-parity-issue.yml
+
+on:
+ workflow_dispatch:
+ inputs:
+ prDescription:
+ description: PR description
+ default: 'No description provided'
+ required: true
+ prNumber:
+ description: PR number
+ required: true
+ prTitle:
+ description: PR title
+ required: true
+ sourceRepo:
+ description: repository PR is sourced from
+ required: true
+
+jobs:
+ createIssue:
+ name: create issue
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: joshgummersall/create-issue@main
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ title: |
+ port: ${{ github.event.inputs.prTitle }} (#${{ github.event.inputs.prNumber }})
+ labels: |
+ ["parity", "needs-triage", "ExemptFromDailyDRIReport"]
+ body: |
+ The changes in [${{ github.event.inputs.prTitle }} (#${{ github.event.inputs.prNumber }})](https://github.com/${{ github.event.inputs.sourceRepo }}/pull/${{ github.event.inputs.prNumber }}) may need to be ported to maintain parity with `${{ github.event.inputs.sourceRepo }}`.
+
+
+ ${{ github.event.inputs.prDescription }}
+
+
+ Please review and, if necessary, port the changes.
diff --git a/.github/workflows/pr-style.yml b/.github/workflows/pr-style.yml
new file mode 100644
index 000000000..51dbf531a
--- /dev/null
+++ b/.github/workflows/pr-style.yml
@@ -0,0 +1,18 @@
+name: pr-style.yml
+
+on:
+ pull_request:
+ types: [opened, edited, synchronize]
+
+jobs:
+ prStyle:
+ name: pr-style
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: joshgummersall/pr-style@main
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ require_issue: "true"
+ skip_authors: "dependabot"
diff --git a/.gitignore b/.gitignore
index 9d6304e11..d80f28b79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,4 +53,19 @@ target
Thumbs.db
# reduced pom files should not be included
-dependency-reduced-pom.xml
\ No newline at end of file
+dependency-reduced-pom.xml
+*.factorypath
+.vscode/settings.json
+pom.xml.versionsBackup
+libraries/swagger/generated
+/javabotframework_pub.gpg
+/javabotframework_sec.gpg
+/private.pgp
+/privatekey.txt
+/public.pgp
+/.vs/slnx.sqlite
+/.vs/botbuilder-java/v16/.suo
+/.vs/ProjectSettings.json
+/.vs/botbuilder-java/v16/TestStore/0/000.testlog
+/.vs/botbuilder-java/v16/TestStore/0/testlog.manifest
+/.vs/VSWorkspaceState.json
diff --git a/.travis.yml b/.travis.yml
index dff5f3a5d..c8de2a4ae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,3 @@
language: java
+before_install:
+ - mvn resources:resources
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index c5f3f6b9c..000000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "java.configuration.updateBuildConfiguration": "interactive"
-}
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..f9ba8cf65
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,9 @@
+# Microsoft Open Source Code of Conduct
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+
+Resources:
+
+- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
+- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
+- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
diff --git a/Contributing.md b/Contributing.md
new file mode 100644
index 000000000..41a2e6153
--- /dev/null
+++ b/Contributing.md
@@ -0,0 +1,23 @@
+# Instructions for Contributing Code
+
+## Contributing bug fixes and features
+
+The Bot Framework team is currently accepting contributions in the form of bug fixes and new
+features. Any submission must have an issue tracking it in the issue tracker that has
+ been approved by the Bot Framework team. Your pull request should include a link to
+ the bug that you are fixing. If you've submitted a PR for a bug, please post a
+ comment in the bug to avoid duplication of effort.
+
+## Legal
+
+If your contribution is more than 15 lines of code, you will need to complete a Contributor
+License Agreement (CLA). Briefly, this agreement testifies that you are granting us permission
+ to use the submitted change according to the terms of the project's license, and that the work
+ being submitted is under appropriate copyright.
+
+Please submit a Contributor License Agreement (CLA) before submitting a pull request.
+You may visit https://cla.azure.com to sign digitally. Alternatively, download the
+agreement ([Microsoft Contribution License Agreement.docx](https://www.codeplex.com/Download?ProjectName=typescript&DownloadId=822190) or
+ [Microsoft Contribution License Agreement.pdf](https://www.codeplex.com/Download?ProjectName=typescript&DownloadId=921298)), sign, scan,
+ and email it back to . Be sure to include your github user name along with the agreement. Once we have received the
+ signed CLA, we'll review the request.
\ No newline at end of file
diff --git a/README.md b/README.md
index 346447519..7c90ffb18 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,129 @@
+# 
-# Bot Builder SDK v4 (Java)
+**The Bot Framework Java SDK is being retired with final long-term support ending in November 2023, after which this repository will be archived. There will be no further feature development, with only critical security and bug fixes within this repository being undertaken. Existing bots built with this SDK will continue to function. For all new bot development we recommend that you adopt [Power Virtual Agents](https://powervirtualagents.microsoft.com/en-us/blog/the-future-of-bot-building/) or use the [Bot Framework C#](https://github.com/microsoft/botbuilder-dotnet) or [Bot Framework JavaScript](https://github.com/microsoft/botbuilder-js) SDKs.**
-[](https://travis-ci.org/Microsoft/botbuilder-java)
-[](https://github.com/Microsoft/botbuilder-java/wiki/Roadmap)
+This repository contains code for the Java version of the [Microsoft Bot Framework SDK](https://github.com/Microsoft/botframework-sdk), which is part of the Microsoft Bot Framework - a comprehensive framework for building enterprise-grade conversational AI experiences.
-This repository contains code for the Java version of the [Microsoft Bot Builder SDK](https://github.com/Microsoft/BotBuilder). The 4.x version of the SDK is being actively developed and should therefore be used for **EXPERIMENTATION PURPOSES ONLY**.
-In addition to the Java SDK, Bot Builder supports creating bots in other popular programming languages like [.Net SDK](https://github.com/Microsoft/botbuilder-dotnet), [JavaScript](https://github.com/Microsoft/botbuilder-js), and [Python](https://github.com/Microsoft/botbuilder-python).
+This SDK enables developers to model conversation and build sophisticated bot applications using Java. SDKs for [.NET](https://github.com/Microsoft/botbuilder-dotnet), [Python](https://github.com/Microsoft/botbuilder-python) and [JavaScript](https://github.com/Microsoft/botbuilder-js) are also available.
-To get started see the [wiki](https://github.com/Microsoft/botbuilder-java/wiki) for the v4 SDK.
+To get started building bots using the SDK, see the [Azure Bot Service Documentation](https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0). If you are an existing user, then you can also [find out what's new with Bot Framework](https://docs.microsoft.com/en-us/azure/bot-service/what-is-new?view=azure-bot-service-4.0).
-## Contributing
+For more information jump to a section below.
-This project welcomes contributions and suggestions. Most contributions require you to agree to a
-Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
-the rights to use your contribution. For details, visit https://cla.microsoft.com.
+- [Bot Framework for Java](#)
+ - [Build Status](#build-status)
+ - [Getting Started](#getting-started)
+ - [Prerequisites](#prerequisites)
+ - [Clone](#clone)
+ - [Build and test locally](#build-and-test-locally)
+ - [Linting rules](#linting-rules)
+ - [Getting support and providing feedback](#getting-support-and-providing-feedback)
+ - [Github issues](#github-issues)
+ - [Stack overflow](#stack-overflow)
+ - [Azure Support](#azure-support)
+ - [Twitter](#twitter)
+ - [Gitter Chat Room](#gitter-chat-room)
+ - [Contributing and our code of conduct](#contributing-and-our-code-of-conduct)
+ - [Reporting Security Issues](#reporting-security-issues)
-When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
-a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
-provided by the bot. You will only need to do this once across all repos using our CLA.
+## Build Status
+
+ | Branch | Description | Build Status | Coverage Status |
+ |--------|-------------|--------------|-----------------|
+ |Main | 4.15.* Builds | [](https://fuselabs.visualstudio.com/SDK_v4/_build/latest?definitionId=1202&branchName=main) | [](https://coveralls.io/github/microsoft/botbuilder-java?branch=823847c676b7dbb0fa348a308297ae375f5141ef) |
+
+## Getting Started
+To get started building bots using the SDK, see the [Azure Bot Service Documentation](https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0).
+
+The [Bot Framework Samples](https://github.com/microsoft/botbuilder-samples) includes a rich set of samples repository.
+
+If you want to debug an issue, would like to [contribute](#contributing), or understand how the Bot Builder SDK works, instructions for building and testing the SDK are below.
+
+### Prerequisites
+- [Git](https://git-scm.com/downloads)
+- [Java](https://www.azul.com/downloads/zulu/)
+- [Maven](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html)
+
+### Clone
+Clone a copy of the repo:
+```bash
+git clone https://github.com/Microsoft/botbuilder-java.git
+```
+Change to the SDK's directory:
+```bash
+cd botbuilder-java
+```
+
+Now at the command prompt type:
+```bash
+mvn clean install
+```
+
+### Build and test locally
+Any IDE that can import and work with Maven projects should work. As a matter of practice we use the command line to perform Maven builds. If your IDE can be configured to defer build and run to Maven it should also work.
+- Java
+ - We use the [Azul JDK 8](https://www.azul.com/downloads/azure-only/zulu/?version=java-8-lts&architecture=x86-64-bit&package=jdk) to build and test with. While not a requirement to develop the SDK with, it is recommended as this is what Azure is using for Java 1.8. If you do install this JDK, make sure your IDE is targeting that JVM, and your path (from command line) and JAVA_HOME point to that.
+
+- Visual Studio Code
+ - Extensions
+ - Java Extension Pack by Microsoft
+ - EditorConfig for VS Code by EditorConfig (Recommended)
+
+- IntelliJ
+ - Extensions
+ - Checkstyle by IDEA
+ - Recommended setup
+ - When importing the SDK for the first time, make sure "Auto import" is checked.
+
+### Linting rules
+
+This project uses linting rules to enforce code standardization. These rules are specified in the file [bot-checkstyle.xml](./etc/bot-checkstyle.xml) with [CheckStyle](https://checkstyle.org/) and are hooked to Maven's build cycle.
+
+**INFO**: Since the CheckStyle and PMD plugins are hooked into the build cycle, this makes the build **fail** in cases where there are linting warnings in the project files. Errors will be in the file ./target/checkstyle-result.xml and ./target/pmd.xml.
+
+CheckStyle is available in different flavours:
+- [Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=shengchen.vscode-checkstyle)
+- [IntelliJ IDEA plugin](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea)
+- [Eclipse plugin](https://checkstyle.org/eclipse-cs)
+- [CLI Tool](https://checkstyle.org/cmdline.html)
+
+**INFO**: Be sure to configure your IDE to use the file [bot-checkstyle.xml](./etc/bot-checkstyle.xml) instead of the default rules.
+
+## Getting support and providing feedback
+Below are the various channels that are available to you for obtaining support and providing feedback. Please pay carful attention to which channel should be used for which type of content. e.g. general "how do I..." questions should be asked on Stack Overflow, Twitter or Gitter, with GitHub issues being for feature requests and bug reports.
+
+### Github issues
+[Github issues](https://github.com/Microsoft/botbuilder-python/issues) should be used for bugs and feature requests.
+
+### Stack overflow
+[Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) is a great place for getting high-quality answers. Our support team, as well as many of our community members are already on Stack Overflow providing answers to 'how-to' questions.
+
+### Azure Support
+If you issues relates to [Azure Bot Service](https://azure.microsoft.com/en-gb/services/bot-service/), you can take advantage of the available [Azure support options](https://azure.microsoft.com/en-us/support/options/).
+
+### Twitter
+We use the [@botframework](https://twitter.com/botframework) account on twitter for announcements and members from the development team watch for tweets for @botframework.
+
+### Gitter Chat Room
+The [Gitter Channel](https://gitter.im/Microsoft/BotBuilder) provides a place where the Community can get together and collaborate.
+
+## Contributing and our code of conduct
+We welcome contributions and suggestions. Please see our [contributing guidelines](./Contributing.md) for more information.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-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.
+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.
## Reporting Security Issues
+Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC)
+at [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some
+ reason you do not, please follow up via email to ensure we received your original message. Further information,
+ including the [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the
+[Security TechCenter](https://technet.microsoft.com/en-us/security/default).
-Security issues and bugs should be reported privately, via email, to the Microsoft Security
-Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
-receive a response within 24 hours. If for some reason you do not, please follow up via
-email to ensure we received your original message. Further information, including the
-[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in
-the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Licensed under the [MIT](./LICENSE.md) License.
-## License
-Copyright (c) Microsoft Corporation. All rights reserved.
-Licensed under the [MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) License.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..e138ec5d6
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+
diff --git a/STATUS.md b/STATUS.md
new file mode 100644
index 000000000..88e0b4c9a
--- /dev/null
+++ b/STATUS.md
@@ -0,0 +1,70 @@
+# Java Bot Framework
+
+The current release is **Preview 8**.
+
+## Core Packages
+
+| Package | Status
+| :------------- |:-------------
+| bot-schema | Preview 2
+| bot-connector | Preview 2
+| bot-integration-core | Preview 2
+| bot-builder | Preview 3
+| bot-builder-teams | Preview 5
+| bot-schema-teams | Preview 5
+| bot-connector-teams | Preview 5
+| bot-dialog | Preview 8
+| bot-dialog-adaptive | TBD
+| bot-dialog-declarative | TBD
+| bot-streaming-extensions | TBD
+| bot-ai-luis-v3 | Preview 8
+| bot-ai-qna | In Progress
+| bot-applicationinsights | Not Started
+| bot-azure | In Progress
+
+## Samples
+| Package | Status
+| ------------- |:-------------
+| 02.echo-bot | Preview 3
+| 03.welcome-user | Preview 3
+| 05.multi-turn-prompt | Preview 8
+| 06.using-cards | Preview 8
+| 07.using-adaptive-cards |
+| 08.suggested-actions | Preview 3
+| 11.qnamaker |
+| 13.core-bot |
+| 14.nlp-with-dispatch |
+| 15.handling-attachments |
+| 16.proactive-messages | Preview 3
+| 17.multilingual-bot | Preview 8
+| 18.bot-authentication | Preview 8
+| 19.custom-dialogs | Preview 8
+| 21.corebot-app-insights |
+| 23.facebook-events |
+| 24.bot-authentication-msgraph | Preview 8
+| 40.timex-resolution |
+| 42.scaleout |
+| 43.complex-dialog | Preview 8
+| 44.prompt-users-for-input | Preview 8
+| 45.state-management | Preview 3
+| 46.teams-auth | Preview 8
+| 47.inspection | Preview 3
+| 48.qnamaker-active-learning-bot |
+| 49.qnamaker-all-features |
+| 50.teams-messaging-extensions-search | Preview 5
+| 51.teams-messaging-extensions-action | Preview 5
+| 52.teams-messaging-extensions-search-auth-config | Preview 5
+| 53.teams-messaging-extensions-action-preview | Preview 5
+| 54.teams-task-module | Preview 5
+| 55.teams-link-unfurling | Preview 5
+| 56.teams-file-upload | Preview 5
+| 57.teams-conversation-bot | Preview 5
+| 58.teams-start-new-thread-in-channel | Preview 5
+| servlet-echo | Preview 2
+
+## Build Prerequisites
+
+- [Java 1.8](https://docs.microsoft.com/en-us/azure/java/jdk/java-jdk-install)
+ - Should be able to execute `java -version` from command line.
+- [Maven](https://maven.apache.org/install.html)
+ - Should be able to execute `mvn -version` from command line.
diff --git a/docs/media/BotFrameworkJava_header.png b/docs/media/BotFrameworkJava_header.png
new file mode 100644
index 000000000..4feb13007
Binary files /dev/null and b/docs/media/BotFrameworkJava_header.png differ
diff --git a/etc/bot-checkstyle.xml b/etc/bot-checkstyle.xml
new file mode 100644
index 000000000..a379f3c79
--- /dev/null
+++ b/etc/bot-checkstyle.xml
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etc/botframework-java-formatter.xml b/etc/botframework-java-formatter.xml
new file mode 100644
index 000000000..349eea9a8
--- /dev/null
+++ b/etc/botframework-java-formatter.xml
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/exec.patch b/exec.patch
deleted file mode 100644
index bce957df9..000000000
--- a/exec.patch
+++ /dev/null
@@ -1,127 +0,0 @@
-diff --git a/libraries/bot-connector/pom.xml b/libraries/bot-connector/pom.xml
-index a51605f..1f7188b 100644
---- a/libraries/bot-connector/pom.xml
-+++ b/libraries/bot-connector/pom.xml
-@@ -4,8 +4,15 @@
-
- com.microsoft.bot.connector
- bot-connector
-- 4.0.0
- jar
-+ 4.0.0
-+
-+
-+ com.microsoft.bot
-+ bot-parent
-+ 4.0.0
-+ ../../
-+
-
- bot-connector
- http://maven.apache.org
-diff --git a/libraries/botbuilder-schema/pom.xml b/libraries/botbuilder-schema/pom.xml
-index d58364f..9ca96e4 100644
---- a/libraries/botbuilder-schema/pom.xml
-+++ b/libraries/botbuilder-schema/pom.xml
-@@ -4,8 +4,15 @@
-
- com.microsoft.bot.schema
- botbuilder-schema
-- 4.0.0
- jar
-+ 4.0.0
-+
-+
-+ com.microsoft.bot
-+ bot-parent
-+ 4.0.0
-+ ../../
-+
-
- botbuilder-schema
- http://maven.apache.org
-diff --git a/pom.xml b/pom.xml
-index 5006517..f5a904f 100644
---- a/pom.xml
-+++ b/pom.xml
-@@ -12,8 +12,8 @@
- https://github.com/Microsoft/botbuilder-java
-
-
-- ./libraries/botbuilder-schema
-- ./libraries/bot-connector
-- ./samples/bot-connector-sample
-+ libraries/botbuilder-schema
-+ libraries/bot-connector
-+ samples/bot-connector-sample
-
-
-\ No newline at end of file
-diff --git a/samples/bot-connector-sample/pom.xml b/samples/bot-connector-sample/pom.xml
-index 74c896a..fc37670 100644
---- a/samples/bot-connector-sample/pom.xml
-+++ b/samples/bot-connector-sample/pom.xml
-@@ -4,8 +4,15 @@
-
- com.microsoft.bot.connector.sample
- bot-connector-sample
-- 1.0.0
- jar
-+ 1.0.0
-+
-+
-+ com.microsoft.bot
-+ bot-parent
-+ 4.0.0
-+ ../../
-+
-
- bot-connector-sample
- http://maven.apache.org
-@@ -36,6 +43,16 @@
- jackson-datatype-jsr310
- 2.9.2
-
-+
-+ org.slf4j
-+ slf4j-api
-+ LATEST
-+
-+
-+ org.slf4j
-+ slf4j-simple
-+ LATEST
-+
-
- com.microsoft.bot.schema
- botbuilder-schema
-@@ -59,6 +76,14 @@
- 1.8
-
-
-+
-+ org.codehaus.mojo
-+ exec-maven-plugin
-+ 1.6.0
-+
-+ com.microsoft.bot.connector.sample.App
-+
-+
-
-
-
-diff --git a/samples/bot-connector-sample/src/main/java/com/microsoft/bot/connector/sample/App.java b/samples/bot-connector-sample/src/main/java/com/microsoft/bot/connector/sample/App.java
-index c0ef0ea..738c52c 100644
---- a/samples/bot-connector-sample/src/main/java/com/microsoft/bot/connector/sample/App.java
-+++ b/samples/bot-connector-sample/src/main/java/com/microsoft/bot/connector/sample/App.java
-@@ -24,8 +24,8 @@ import java.util.logging.Logger;
-
- public class App {
- private static final Logger LOGGER = Logger.getLogger( App.class.getName() );
-- private static String appId = "<-- app id -->";
-- private static String appPassword = "<-- app password -->";
-+ private static String appId = "39619a59-5a0c-4f9b-87c5-816c648ff357"; // <-- app id -->
-+ private static String appPassword = "b90er1BC2xp9Y5Exqwj8qwf"; // <-- app password -->
-
- public static void main( String[] args ) throws IOException {
- CredentialProvider credentialProvider = new CredentialProviderImpl(appId, appPassword);
diff --git a/generators/.gitignore b/generators/.gitignore
new file mode 100644
index 000000000..ba2a97b57
--- /dev/null
+++ b/generators/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+coverage
diff --git a/generators/LICENSE.md b/generators/LICENSE.md
new file mode 100644
index 000000000..506ab97e5
--- /dev/null
+++ b/generators/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Microsoft Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/generators/README.md b/generators/README.md
new file mode 100644
index 000000000..c86d8685f
--- /dev/null
+++ b/generators/README.md
@@ -0,0 +1,126 @@
+# generator-botbuilder-java
+
+Yeoman generator for [Bot Framework v4](https://dev.botframework.com). Will let you quickly set up a conversational AI bot
+using core AI capabilities.
+
+## About
+
+`generator-botbuilder-java` will help you build new conversational AI bots using the [Bot Framework v4](https://dev.botframework.com).
+
+## Templates
+
+The generator supports three different template options. The table below can help guide which template is right for you.
+
+| 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 are 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 [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding. This template creates a bot that can extract places and dates to book a flight. |
+
+### How to Choose a Template
+
+| Template | When This Template is a Good Choice |
+| -------- | -------- |
+| Echo Bot | You are new to Bot Framework v4 and want a working bot with minimal features. |
+| Empty Bot | You are a seasoned Bot Framework v4 developer. You've built bots before, and want the minimum skeleton of a bot. |
+| Core Bot | You are a medium to advanced user of Bot Framework v4 and want to start integrating language understanding as well as multi-turn dialogs in your bots. |
+
+### Template Overview
+
+#### Echo Bot Template
+
+The Echo Bot template is slightly more than the a classic "Hello World!" example, but not by much. This template shows the basic structure of a bot, how a bot receives messages from a user, and how a bot sends messages to a user. The bot will "echo" back to the user, what the user says to the bot. It is a good choice for first time, new to Bot Framework v4 developers.
+
+#### Empty Bot Template
+
+The Empty Bot template is the minimal skeleton code for a bot. It provides a stub `onTurn` handler but does not perform any actions. If you are experienced writing bots with Bot Framework v4 and want the minimum scaffolding, the Empty template is for you.
+
+#### Core Bot Template
+
+The Core Bot template uses [LUIS](https://www.luis.ai) to implement core AI capabilities, a multi-turn conversation using Dialogs, handles user interruptions, and prompts for and validate requests for information from the user. This template implements a basic three-step waterfall dialog, where the first step asks the user for an input to book a flight, then asks the user if the information is correct, and finally confirms the booking with the user. Choose this template if want to create an advanced bot that can extract information from the user's input.
+
+## Installation
+
+1. Install [Yeoman](http://yeoman.io) using [npm](https://www.npmjs.com) (we assume you have pre-installed [node.js](https://nodejs.org/)).
+
+ ```bash
+ # Make sure both are installed globally
+ npm install -g yo
+ ```
+
+2. Install generator-botbuilder-java by typing the following in your console:
+
+ ```bash
+ # Make sure both are installed globally
+ npm install -g generator-botbuilder-java
+ ```
+
+3. Verify that Yeoman and generator-botbuilder-java have been installed correctly by typing the following into your console:
+
+ ```bash
+ yo botbuilder-java --help
+ ```
+
+## Usage
+
+### Creating a New Bot Project
+
+When the generator is launched, it will prompt for the information required to create a new bot.
+
+```bash
+# Run the generator in interactive mode
+yo botbuilder-java
+```
+
+### Generator Command Line Options
+
+The generator supports a number of command line options that can be used to change the generator's default options or to pre-seed a prompt.
+
+| 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 `empty`, or `echo`. See [https://aka.ms/botbuilder-generator](https://aka.ms/botbuilder-generator) for additional information regarding the different template option and their functional differences. |
+| --noprompt | The generator will not 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. |
+
+#### Example Using Command Line Options
+
+This example shows how to pass command line options to the generator, setting the default language to TypeScript and the default template to Core.
+
+```bash
+# Run the generator defaulting the pacakge name and the template
+yo botbuilder-java --P "com.mycompany.bot" --T "echo"
+```
+
+### Generating a Bot Using --noprompt
+
+The generator can be run in `--noprompt` mode, which can be used for automated bot creation. When run in `--noprompt` mode, the generator can be configured using command line options as documented above. If a command line option is ommitted a reasonable default will be used. In addition, passing the `--noprompt` option will cause the generator to create a new bot project without prompting for confirmation before generating the bot.
+
+#### Default Options
+
+| Command line Option | Default Value |
+| ------------------- | ----------- |
+| --botname, -N | `echo` |
+| --packageName, -p | `echo` |
+| --template, -T | `echo` |
+
+#### Examples Using --noprompt
+
+This example shows how to run the generator in --noprompt mode, setting all required options on the command line.
+
+```bash
+# Run the generator, setting all command line options
+yo botbuilder-java --noprompt -N "MyEchoBot" -P "com.mycompany.bot.echo" -T "echo"
+```
+
+This example shows how to run the generator in --noprompt mode, using all the default command line options. The generator will create a bot project using all the default values specified in the **Default Options** table above.
+
+```bash
+# Run the generator using all default options
+yo botbuilder-java --noprompt
+```
+
+## Logging Issues and Providing Feedback
+
+Issues and feedback about the botbuilder generator can be submitted through the project's [GitHub Issues](https://github.com/Microsoft/botbuilder-java/issues) page.
diff --git a/generators/generators/app/index.js b/generators/generators/app/index.js
new file mode 100644
index 000000000..13df547aa
--- /dev/null
+++ b/generators/generators/app/index.js
@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+'use strict';
+const pkg = require('../../package.json');
+const Generator = require('yeoman-generator');
+const path = require('path');
+const chalk = require('chalk');
+const mkdirp = require('mkdirp');
+const _ = require('lodash');
+
+const BOT_TEMPLATE_NAME_EMPTY = 'Empty Bot';
+const BOT_TEMPLATE_NAME_SIMPLE = 'Echo Bot';
+const BOT_TEMPLATE_NAME_CORE = 'Core Bot';
+
+const BOT_TEMPLATE_NOPROMPT_EMPTY = 'empty';
+const BOT_TEMPLATE_NOPROMPT_SIMPLE = 'echo';
+const BOT_TEMPLATE_NOPROMPT_CORE = 'core';
+
+const bigBot =
+ ` ╭─────────────────────────────╮\n` +
+ ` ` +
+ chalk.blue.bold(`//`) +
+ ` ` +
+ chalk.blue.bold(`\\\\`) +
+ ` │ Welcome to the │\n` +
+ ` ` +
+ chalk.blue.bold(`//`) +
+ ` () () ` +
+ chalk.blue.bold(`\\\\`) +
+ ` │ Microsoft Java Bot Builder │\n` +
+ ` ` +
+ chalk.blue.bold(`\\\\`) +
+ ` ` +
+ chalk.blue.bold(`//`) +
+ ` /│ generator! │\n` +
+ ` ` +
+ chalk.blue.bold(`\\\\`) +
+ ` ` +
+ chalk.blue.bold(`//`) +
+ ` ╰─────────────────────────────╯\n` +
+ ` v${pkg.version}`;
+
+const tinyBot =
+ ` ` + chalk.blue.bold(`<`) + ` ** ` + chalk.blue.bold(`>`) + ` `;
+
+module.exports = class extends Generator {
+ constructor(args, opts) {
+ super(args, opts);
+
+ // allocate an object that we can use to store our user prompt values from our askFor* functions
+ this.templateConfig = {};
+
+ // configure the commandline options
+ this._configureCommandlineOptions();
+ }
+
+ initializing() {
+ // give the user some data before we start asking them questions
+ this.log(bigBot);
+ }
+
+ prompting() {
+ // if we're told to not prompt, then pick what we need and return
+ if (this.options.noprompt) {
+ // this function will throw if it encounters errors/invalid options
+ return this._verifyNoPromptOptions();
+ }
+
+ const userPrompts = this._getPrompts();
+ async function executePrompts([prompt, ...rest]) {
+ if (prompt) {
+ await prompt();
+ return executePrompts(rest);
+ }
+ }
+
+ return executePrompts(userPrompts);
+ }
+
+ writing() {
+ // if the user confirmed their settings, then lets go ahead
+ // an install module dependencies
+ if (this.templateConfig.finalConfirmation === true) {
+ const botName = this.templateConfig.botName;
+ const packageName = this.templateConfig.packageName.replace(/-/g, '_').toLowerCase();
+ const packageTree = packageName.replace(/\./g, '/');
+ const artifact = _.kebabCase(this.templateConfig.botName).replace(/([^a-z0-9-]+)/gi, ``);
+ const directoryName = _.camelCase(this.templateConfig.botName);
+ const template = this.templateConfig.template.toLowerCase();
+
+ if (path.basename(this.destinationPath()) !== directoryName) {
+ mkdirp.sync(directoryName);
+ this.destinationRoot(this.destinationPath(directoryName));
+ }
+
+ // Copy the project tree
+ this.fs.copyTpl(
+ this.templatePath(path.join(template, 'project', '**')),
+ this.destinationPath(),
+ {
+ botName,
+ packageName,
+ artifact
+ }
+ );
+
+ // Copy main source
+ this.fs.copyTpl(
+ this.templatePath(path.join(template, 'src/main/java/**')),
+ this.destinationPath(path.join('src/main/java', packageTree)),
+ {
+ packageName
+ }
+ );
+
+ // Copy test source
+ this.fs.copyTpl(
+ this.templatePath(path.join(template, 'src/test/java/**')),
+ this.destinationPath(path.join('src/test/java', packageTree)),
+ {
+ packageName
+ }
+ );
+ }
+ }
+
+ end() {
+ if (this.templateConfig.finalConfirmation === true) {
+ this.log(chalk.green('------------------------ '));
+ this.log(chalk.green(' Your new bot is ready! '));
+ this.log(chalk.green('------------------------ '));
+ this.log(`Your bot should be in a directory named "${_.camelCase(this.templateConfig.botName)}"`);
+ this.log('Open the ' + chalk.green.bold('README.md') + ' to learn how to run your bot. ');
+ this.log('Thank you for using the Microsoft Bot Framework. ');
+ this.log(`\n${tinyBot} The Bot Framework Team`);
+ } else {
+ this.log(chalk.red.bold('-------------------------------- '));
+ this.log(chalk.red.bold(' New bot creation was canceled. '));
+ this.log(chalk.red.bold('-------------------------------- '));
+ this.log('Thank you for using the Microsoft Bot Framework. ');
+ this.log(`\n${tinyBot} The Bot Framework Team`);
+ }
+ }
+
+ _configureCommandlineOptions() {
+ this.option('botName', {
+ desc: 'The name you want to give to your bot',
+ type: String,
+ default: 'echo',
+ alias: 'N'
+ });
+
+ this.option('packageName', {
+ desc: `What's the fully qualified package name of your bot?`,
+ type: String,
+ default: 'com.mycompany.echo',
+ alias: 'P'
+ });
+
+ const templateDesc = `The initial bot capabilities. (${BOT_TEMPLATE_NAME_EMPTY} | ${BOT_TEMPLATE_NAME_SIMPLE} | ${BOT_TEMPLATE_NAME_CORE})`;
+ this.option('template', {
+ desc: templateDesc,
+ type: String,
+ default: BOT_TEMPLATE_NAME_SIMPLE,
+ alias: 'T'
+ });
+
+ this.argument('noprompt', {
+ desc: 'Do not prompt for any information or confirmation',
+ type: Boolean,
+ required: false,
+ default: false
+ });
+ }
+
+ _getPrompts() {
+ return [
+ // ask the user to name their bot
+ async () => {
+ return this.prompt({
+ type: 'input',
+ name: 'botName',
+ message: `What's the name of your bot?`,
+ default: (this.options.botName ? this.options.botName : 'echo')
+ }).then(answer => {
+ // store the botname description answer
+ this.templateConfig.botName = answer.botName;
+ });
+ },
+
+ // ask for package name
+ async () => {
+ return this.prompt({
+ type: 'input',
+ name: 'packageName',
+ message: `What's the fully qualified package name of your bot?`,
+ default: (this.options.packageName ? this.options.packageName : 'com.mycompany.echo')
+ }).then(answer => {
+ // store the package name description answer
+ this.templateConfig.packageName = answer.packageName;
+ });
+ },
+
+
+ // ask the user which bot template we should use
+ async () => {
+ return this.prompt({
+ type: 'list',
+ name: 'template',
+ message: 'Which template would you like to start with?',
+ choices: [
+ {
+ name: BOT_TEMPLATE_NAME_SIMPLE,
+ value: BOT_TEMPLATE_NOPROMPT_SIMPLE
+ },
+ {
+ name: BOT_TEMPLATE_NAME_EMPTY,
+ value: BOT_TEMPLATE_NOPROMPT_EMPTY
+ },
+ {
+ name: BOT_TEMPLATE_NAME_CORE,
+ value: BOT_TEMPLATE_NOPROMPT_CORE
+ }
+ ],
+ default: (this.options.template ? _.toLower(this.options.template) : BOT_TEMPLATE_NOPROMPT_SIMPLE)
+ }).then(answer => {
+ // store the template prompt answer
+ this.templateConfig.template = answer.template;
+ });
+ },
+
+ // ask the user for final confirmation before we generate their bot
+ async () => {
+ return this.prompt({
+ type: 'confirm',
+ name: 'finalConfirmation',
+ message: 'Looking good. Shall I go ahead and create your new bot?',
+ default: true
+ }).then(answer => {
+ // store the finalConfirmation prompt answer
+ this.templateConfig.finalConfirmation = answer.finalConfirmation;
+ });
+ }
+ ];
+ }
+
+ // if we're run with the --noprompt option, verify that all required options were supplied.
+ // throw for missing options, or a resolved Promise
+ _verifyNoPromptOptions() {
+ this.templateConfig = _.pick(this.options, ['botName', 'packageName', 'template'])
+
+ // validate we have what we need, or we'll need to throw
+ if (!this.templateConfig.botName) {
+ throw new Error('Must specify a name for your bot when using --noprompt argument. Use --botName or -N');
+ }
+ if (!this.templateConfig.packageName) {
+ throw new Error('Must specify a package name for your bot when using --noprompt argument. Use --packageName or -P');
+ }
+
+ // make sure we have a supported template
+ const template = (this.templateConfig.template ? _.toLower(this.templateConfig.template) : undefined);
+ const tmplEmpty = _.toLower(BOT_TEMPLATE_NOPROMPT_EMPTY);
+ const tmplSimple = _.toLower(BOT_TEMPLATE_NOPROMPT_SIMPLE);
+ const tmplCore = _.toLower(BOT_TEMPLATE_NOPROMPT_CORE);
+ if (!template || (template !== tmplEmpty && template !== tmplSimple && template !== tmplCore)) {
+ throw new Error('Must specify a template when using --noprompt argument. Use --template or -T');
+ }
+
+ // when run using --noprompt and we have all the required templateConfig, then set final confirmation to true
+ // so we can go forward and create the new bot without prompting the user for confirmation
+ this.templateConfig.finalConfirmation = true;
+
+ return Promise.resolve();
+ }
+};
diff --git a/generators/generators/app/templates/core/project/README-LUIS.md b/generators/generators/app/templates/core/project/README-LUIS.md
new file mode 100644
index 000000000..12bc78ed0
--- /dev/null
+++ b/generators/generators/app/templates/core/project/README-LUIS.md
@@ -0,0 +1,216 @@
+# Setting up LUIS via CLI:
+
+This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App.
+
+> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_
+> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_
+> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_
+
+ [Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app
+ [Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app
+
+## Table of Contents:
+
+- [Prerequisites](#Prerequisites)
+- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application)
+- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application)
+
+___
+
+## [Prerequisites](#Table-of-Contents):
+
+#### Install Azure CLI >=2.0.61:
+
+Visit the following page to find the correct installer for your OS:
+- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
+
+#### Install LUIS CLI >=2.4.0:
+
+Open a CLI of your choice and type the following:
+
+```bash
+npm i -g luis-apis@^2.4.0
+```
+
+#### LUIS portal account:
+
+You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions].
+
+After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID.
+
+ [LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions]
+ [LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key
+
+___
+
+## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents)
+
+### 1. Import the local LUIS application to luis.ai
+
+```bash
+luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json"
+```
+
+Outputs the following JSON:
+
+```json
+{
+ "id": "########-####-####-####-############",
+ "name": "FlightBooking",
+ "description": "A LUIS model that uses intent and entities.",
+ "culture": "en-us",
+ "usageScenario": "",
+ "domain": "",
+ "versionsCount": 1,
+ "createdDateTime": "2019-03-29T18:32:02Z",
+ "endpoints": {},
+ "endpointHitsCount": 0,
+ "activeVersion": "0.1",
+ "ownerEmail": "bot@contoso.com",
+ "tokenizerVersion": "1.0.0"
+}
+```
+
+For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`.
+
+### 2. Train the LUIS Application
+
+```bash
+luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait
+```
+
+### 3. Publish the LUIS Application
+
+```bash
+luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion"
+```
+
+> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast".
+> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai.
+> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth").
+> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions.
+
+ [Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78
+
+Outputs the following:
+
+```json
+ {
+ "versionId": "0.1",
+ "isStaging": false,
+ "endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############",
+ "region": "westus",
+ "assignedEndpointKey": null,
+ "endpointRegion": "westus",
+ "failedRegions": "",
+ "publishedDateTime": "2019-03-29T18:40:32Z",
+ "directVersionPublish": false
+}
+```
+
+To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application.
+
+ [README-LUIS]: ./README-LUIS.md
+
+___
+
+## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents)
+
+### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI
+
+> _Note:_
+> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_
+> ```bash
+> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName"
+> ```
+> _To see a list of valid locations, use `az account list-locations`_
+
+
+```bash
+# Use Azure CLI to create the LUIS Key resource on Azure
+az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
+```
+
+The command will output a response similar to the JSON below:
+
+```json
+{
+ "endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0",
+ "etag": "\"########-####-####-####-############\"",
+ "id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName",
+ "internalId": "################################",
+ "kind": "luis",
+ "location": "westus",
+ "name": "NewLuisResourceName",
+ "provisioningState": "Succeeded",
+ "resourceGroup": "ResourceGroupName",
+ "sku": {
+ "name": "S0",
+ "tier": null
+ },
+ "tags": null,
+ "type": "Microsoft.CognitiveServices/accounts"
+}
+```
+
+
+
+Take the output from the previous command and create a JSON file in the following format:
+
+```json
+{
+ "azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
+ "resourceGroup": "ResourceGroupName",
+ "accountName": "NewLuisResourceName"
+}
+```
+
+### 2. Retrieve ARM access token via Azure CLI
+
+```bash
+az account get-access-token --subscription "AzureSubscriptionGuid"
+```
+
+This will return an object that looks like this:
+
+```json
+{
+ "accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken",
+ "expiresOn": "2200-12-31 23:59:59.999999",
+ "subscription": "AzureSubscriptionGuid",
+ "tenant": "tenant-guid",
+ "tokenType": "Bearer"
+}
+```
+
+The value needed for the next step is the `"accessToken"`.
+
+### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application
+
+```bash
+luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken"
+```
+
+If successful, it should yield a response like this:
+
+```json
+{
+ "code": "Success",
+ "message": "Operation Successful"
+}
+```
+
+### 4. See the LUIS Cognitive Services' keys
+
+```bash
+az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
+```
+
+This will return an object that looks like this:
+
+```json
+{
+ "key1": "9a69####dc8f####8eb4####399f####",
+ "key2": "####f99e####4b1a####fb3b####6b9f"
+}
+```
diff --git a/generators/generators/app/templates/core/project/README.md b/generators/generators/app/templates/core/project/README.md
new file mode 100644
index 000000000..5428395fc
--- /dev/null
+++ b/generators/generators/app/templates/core/project/README.md
@@ -0,0 +1,67 @@
+# <%= botName %>
+
+Bot Framework v4 core bot sample.
+
+This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to:
+
+- Use [LUIS](https://www.luis.ai) to implement core AI capabilities
+- Implement a multi-turn conversation using Dialogs
+- Handle user interruptions for such things as `Help` or `Cancel`
+- Prompt for and validate requests for information from the user
+
+## Prerequisites
+
+This sample **requires** prerequisites in order to run.
+
+### Overview
+
+This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding.
+
+### Create a LUIS Application to enable language understanding
+
+The LUIS model for this example can be found under `cognitiveModels/FlightBooking.json` and the LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
+
+Once you created the LUIS model, update `application.properties` with your `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName`.
+
+```
+ LuisAppId="Your LUIS App Id"
+ LuisAPIKey="Your LUIS Subscription key here"
+ LuisAPIHostName="Your LUIS App region here (i.e: westus.api.cognitive.microsoft.com)"
+```
+
+## To try this sample
+
+- From the root of this project folder:
+ - Build the sample using `mvn package`
+ - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar`
+
+## Testing the bot using Bot Framework Emulator
+
+[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
+
+- Install the latest Bot Framework Emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
+
+### Connect to the bot using Bot Framework Emulator
+
+- Launch Bot Framework Emulator
+- File -> Open Bot
+- Enter a Bot URL of `http://localhost:3978/api/messages`
+
+## Deploy the bot to Azure
+
+To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
+
+## Further reading
+
+- [Bot Framework Documentation](https://docs.botframework.com)
+- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
+- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
+- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
+- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
+- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
+- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
+- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
+- [Azure Portal](https://portal.azure.com)
+- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/)
+- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
+- [Spring Boot](https://spring.io/projects/spring-boot)
diff --git a/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json b/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json
new file mode 100644
index 000000000..603dd06b2
--- /dev/null
+++ b/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json
@@ -0,0 +1,339 @@
+{
+ "luis_schema_version": "3.2.0",
+ "versionId": "0.1",
+ "name": "FlightBooking",
+ "desc": "Luis Model for <%= botName %>",
+ "culture": "en-us",
+ "tokenizerVersion": "1.0.0",
+ "intents": [
+ {
+ "name": "BookFlight"
+ },
+ {
+ "name": "Cancel"
+ },
+ {
+ "name": "GetWeather"
+ },
+ {
+ "name": "None"
+ }
+ ],
+ "entities": [],
+ "composites": [
+ {
+ "name": "From",
+ "children": [
+ "Airport"
+ ],
+ "roles": []
+ },
+ {
+ "name": "To",
+ "children": [
+ "Airport"
+ ],
+ "roles": []
+ }
+ ],
+ "closedLists": [
+ {
+ "name": "Airport",
+ "subLists": [
+ {
+ "canonicalForm": "Paris",
+ "list": [
+ "paris",
+ "cdg"
+ ]
+ },
+ {
+ "canonicalForm": "London",
+ "list": [
+ "london",
+ "lhr"
+ ]
+ },
+ {
+ "canonicalForm": "Berlin",
+ "list": [
+ "berlin",
+ "txl"
+ ]
+ },
+ {
+ "canonicalForm": "New York",
+ "list": [
+ "new york",
+ "jfk"
+ ]
+ },
+ {
+ "canonicalForm": "Seattle",
+ "list": [
+ "seattle",
+ "sea"
+ ]
+ }
+ ],
+ "roles": []
+ }
+ ],
+ "patternAnyEntities": [],
+ "regex_entities": [],
+ "prebuiltEntities": [
+ {
+ "name": "datetimeV2",
+ "roles": []
+ }
+ ],
+ "model_features": [],
+ "regex_features": [],
+ "patterns": [],
+ "utterances": [
+ {
+ "text": "book a flight",
+ "intent": "BookFlight",
+ "entities": []
+ },
+ {
+ "text": "book a flight from new york",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 19,
+ "endPos": 26
+ }
+ ]
+ },
+ {
+ "text": "book a flight from seattle",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 19,
+ "endPos": 25
+ }
+ ]
+ },
+ {
+ "text": "book a hotel in new york",
+ "intent": "None",
+ "entities": []
+ },
+ {
+ "text": "book a restaurant",
+ "intent": "None",
+ "entities": []
+ },
+ {
+ "text": "book flight from london to paris on feb 14th",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 17,
+ "endPos": 22
+ },
+ {
+ "entity": "To",
+ "startPos": 27,
+ "endPos": 31
+ }
+ ]
+ },
+ {
+ "text": "book flight to berlin on feb 14th",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 15,
+ "endPos": 20
+ }
+ ]
+ },
+ {
+ "text": "book me a flight from london to paris",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 22,
+ "endPos": 27
+ },
+ {
+ "entity": "To",
+ "startPos": 32,
+ "endPos": 36
+ }
+ ]
+ },
+ {
+ "text": "bye",
+ "intent": "Cancel",
+ "entities": []
+ },
+ {
+ "text": "cancel booking",
+ "intent": "Cancel",
+ "entities": []
+ },
+ {
+ "text": "exit",
+ "intent": "Cancel",
+ "entities": []
+ },
+ {
+ "text": "find an airport near me",
+ "intent": "None",
+ "entities": []
+ },
+ {
+ "text": "flight to paris",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 10,
+ "endPos": 14
+ }
+ ]
+ },
+ {
+ "text": "flight to paris from london on feb 14th",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 10,
+ "endPos": 14
+ },
+ {
+ "entity": "From",
+ "startPos": 21,
+ "endPos": 26
+ }
+ ]
+ },
+ {
+ "text": "fly from berlin to paris on may 5th",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 9,
+ "endPos": 14
+ },
+ {
+ "entity": "To",
+ "startPos": 19,
+ "endPos": 23
+ }
+ ]
+ },
+ {
+ "text": "go to paris",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 6,
+ "endPos": 10
+ }
+ ]
+ },
+ {
+ "text": "going from paris to berlin",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 11,
+ "endPos": 15
+ },
+ {
+ "entity": "To",
+ "startPos": 20,
+ "endPos": 25
+ }
+ ]
+ },
+ {
+ "text": "i'd like to rent a car",
+ "intent": "None",
+ "entities": []
+ },
+ {
+ "text": "ignore",
+ "intent": "Cancel",
+ "entities": []
+ },
+ {
+ "text": "travel from new york to paris",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "From",
+ "startPos": 12,
+ "endPos": 19
+ },
+ {
+ "entity": "To",
+ "startPos": 24,
+ "endPos": 28
+ }
+ ]
+ },
+ {
+ "text": "travel to new york",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 10,
+ "endPos": 17
+ }
+ ]
+ },
+ {
+ "text": "travel to paris",
+ "intent": "BookFlight",
+ "entities": [
+ {
+ "entity": "To",
+ "startPos": 10,
+ "endPos": 14
+ }
+ ]
+ },
+ {
+ "text": "what's the forecast for this friday?",
+ "intent": "GetWeather",
+ "entities": []
+ },
+ {
+ "text": "what's the weather like for tomorrow",
+ "intent": "GetWeather",
+ "entities": []
+ },
+ {
+ "text": "what's the weather like in new york",
+ "intent": "GetWeather",
+ "entities": []
+ },
+ {
+ "text": "what's the weather like?",
+ "intent": "GetWeather",
+ "entities": []
+ },
+ {
+ "text": "winter is coming",
+ "intent": "None",
+ "entities": []
+ }
+ ],
+ "settings": []
+}
diff --git a/generators/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json
new file mode 100644
index 000000000..196cfb933
--- /dev/null
+++ b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json
@@ -0,0 +1,292 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "groupLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the location of the Resource Group."
+ }
+ },
+ "groupName": {
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the name of the Resource Group."
+ }
+ },
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The name of the App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "newAppServicePlanLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The location of the App Service Plan. Defaults to \"westus\"."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "appServicePlanName": "[parameters('newAppServicePlanName')]",
+ "resourcesLocation": "[parameters('newAppServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
+ },
+ "resources": [
+ {
+ "name": "[parameters('groupName')]",
+ "type": "Microsoft.Resources/resourceGroups",
+ "apiVersion": "2018-05-01",
+ "location": "[parameters('groupLocation')]",
+ "properties": {}
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2018-05-01",
+ "name": "storageDeployment",
+ "resourceGroup": "[parameters('groupName')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
+ ],
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "name": "[variables('appServicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[variables('appServicePlanName')]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ],
+ "outputs": {}
+ }
+ }
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json
new file mode 100644
index 000000000..d6feb0a0f
--- /dev/null
+++ b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json
@@ -0,0 +1,260 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The name of the new App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "appServicePlanLocation": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The location of the App Service Plan."
+ }
+ },
+ "existingAppServicePlan": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Name of the existing App Service Plan used to create the Web App for the bot."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
+ "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
+ "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourcesLocation": "[parameters('appServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
+ },
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "condition": "[not(variables('useExistingAppServicePlan'))]",
+ "name": "[variables('servicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/core/project/pom.xml b/generators/generators/app/templates/core/project/pom.xml
new file mode 100644
index 000000000..8306e22f8
--- /dev/null
+++ b/generators/generators/app/templates/core/project/pom.xml
@@ -0,0 +1,254 @@
+
+
+
+ 4.0.0
+
+ <%= packageName %>
+ <%= artifact %>
+ 1.0.0
+ jar
+
+ ${project.groupId}:${project.artifactId}
+ This package contains a Java Core Bot sample using Spring Boot.
+ http://maven.apache.org
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.0
+
+
+
+
+
+ MIT License
+ http://www.opensource.org/licenses/mit-license.php
+
+
+
+
+
+ Bot Framework Development
+
+ Microsoft
+ https://dev.botframework.com/
+
+
+
+
+ 1.8
+ 1.8
+ 1.8
+ <%= packageName %>.Application
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.4.0
+ test
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+ 2.15.0
+ test
+
+
+
+ com.microsoft.bot
+ bot-integration-spring
+ 4.14.1
+ compile
+
+
+ com.microsoft.bot
+ bot-dialogs
+ 4.14.1
+
+
+ com.microsoft.bot
+ bot-ai-luis-v3
+ 4.14.1
+
+
+
+
+
+ build
+
+ true
+
+
+
+
+ src/main/resources
+ false
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ maven-war-plugin
+ 3.2.3
+
+ src/main/webapp
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+ <%= packageName %>.Application
+
+
+
+
+
+ com.microsoft.azure
+ azure-webapp-maven-plugin
+ 1.12.0
+
+ V2
+ ${groupname}
+ ${botname}
+
+
+ JAVA_OPTS
+ -Dserver.port=80
+
+
+
+ linux
+ Java 8
+ Java SE
+
+
+
+
+ ${project.basedir}/target
+
+ *.jar
+
+
+
+
+
+
+
+
+
+
+
+ publish
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ maven-war-plugin
+ 3.2.3
+
+ src/main/webapp
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+ true
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/core/project/src/main/resources/application.properties b/generators/generators/app/templates/core/project/src/main/resources/application.properties
new file mode 100644
index 000000000..255d7cd56
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/resources/application.properties
@@ -0,0 +1,6 @@
+MicrosoftAppId=
+MicrosoftAppPassword=
+LuisAppId=
+LuisAPIKey=
+LuisAPIHostName=
+server.port=3978
diff --git a/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json b/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json
new file mode 100644
index 000000000..9b6389e39
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+ "type": "AdaptiveCard",
+ "version": "1.0",
+ "body": [
+ {
+ "type": "Image",
+ "url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
+ "size": "stretch"
+ },
+ {
+ "type": "TextBlock",
+ "spacing": "medium",
+ "size": "default",
+ "weight": "bolder",
+ "text": "Welcome to Bot Framework!",
+ "wrap": true,
+ "maxLines": 0
+ },
+ {
+ "type": "TextBlock",
+ "size": "default",
+ "isSubtle": true,
+ "text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
+ "wrap": true,
+ "maxLines": 0
+ }
+ ],
+ "actions": [
+ {
+ "type": "Action.OpenUrl",
+ "title": "Get an overview",
+ "url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0"
+ },
+ {
+ "type": "Action.OpenUrl",
+ "title": "Ask a question",
+ "url": "https://stackoverflow.com/questions/tagged/botframework"
+ },
+ {
+ "type": "Action.OpenUrl",
+ "title": "Learn how to deploy",
+ "url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/core/project/src/main/resources/log4j2.json b/generators/generators/app/templates/core/project/src/main/resources/log4j2.json
new file mode 100644
index 000000000..67c0ad530
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/resources/log4j2.json
@@ -0,0 +1,18 @@
+{
+ "configuration": {
+ "name": "Default",
+ "appenders": {
+ "Console": {
+ "name": "Console-Appender",
+ "target": "SYSTEM_OUT",
+ "PatternLayout": {"pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"}
+ }
+ },
+ "loggers": {
+ "root": {
+ "level": "debug",
+ "appender-ref": {"ref": "Console-Appender","level": "debug"}
+ }
+ }
+ }
+}
diff --git a/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..254272e1c
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..383c19004
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,12 @@
+
+
+ dispatcher
+
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ /WEB-INF/spring/dispatcher-config.xml
+
+ 1
+
\ No newline at end of file
diff --git a/generators/generators/app/templates/core/project/src/main/webapp/index.html b/generators/generators/app/templates/core/project/src/main/webapp/index.html
new file mode 100644
index 000000000..593ac520c
--- /dev/null
+++ b/generators/generators/app/templates/core/project/src/main/webapp/index.html
@@ -0,0 +1,417 @@
+
+
+
+
+
+
+ <%= botName %>
+
+
+
+
+
+
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
Visit Azure
+ Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks
+ like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/core/src/main/java/Application.java b/generators/generators/app/templates/core/src/main/java/Application.java
new file mode 100644
index 000000000..7f99e415d
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/Application.java
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.Bot;
+import com.microsoft.bot.builder.ConversationState;
+import com.microsoft.bot.builder.UserState;
+import com.microsoft.bot.integration.AdapterWithErrorHandler;
+import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
+import com.microsoft.bot.integration.Configuration;
+import com.microsoft.bot.integration.spring.BotController;
+import com.microsoft.bot.integration.spring.BotDependencyConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+
+/**
+ * This is the starting point of the Sprint Boot Bot application.
+ */
+@SpringBootApplication
+
+// Use the default BotController to receive incoming Channel messages. A custom
+// controller could be used by eliminating this import and creating a new
+// org.springframework.web.bind.annotation.RestController.
+// The default controller is created by the Spring Boot container using
+// dependency injection. The default route is /api/messages.
+@Import({BotController.class})
+
+/**
+ * This class extends the BotDependencyConfiguration which provides the default
+ * implementations for a Bot application. The Application class should
+ * override methods in order to provide custom implementations.
+ */
+public class Application extends BotDependencyConfiguration {
+
+ /**
+ * The start method.
+ *
+ * @param args The args.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ /**
+ * Returns the Bot for this application.
+ *
+ *
+ * The @Component annotation could be used on the Bot class instead of this method with the
+ * @Bean annotation.
+ *
+ *
+ * @return The Bot implementation for this application.
+ */
+ @Bean
+ public Bot getBot(
+ Configuration configuration,
+ UserState userState,
+ ConversationState conversationState
+ ) {
+ FlightBookingRecognizer recognizer = new FlightBookingRecognizer(configuration);
+ MainDialog dialog = new MainDialog(recognizer, new BookingDialog());
+ return new DialogAndWelcomeBot<>(conversationState, userState, dialog);
+ }
+
+ /**
+ * Returns a custom Adapter that provides error handling.
+ *
+ * @param configuration The Configuration object to use.
+ * @return An error handling BotFrameworkHttpAdapter.
+ */
+ @Override
+ public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) {
+ return new AdapterWithErrorHandler(configuration);
+ }
+}
+
diff --git a/generators/generators/app/templates/core/src/main/java/BookingDetails.java b/generators/generators/app/templates/core/src/main/java/BookingDetails.java
new file mode 100644
index 000000000..160242106
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/BookingDetails.java
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+/**
+ * The model class to retrieve the information of the booking.
+ */
+public class BookingDetails {
+
+ private String destination;
+ private String origin;
+ private String travelDate;
+
+ /**
+ * Gets the destination of the booking.
+ *
+ * @return The destination.
+ */
+ public String getDestination() {
+ return destination;
+ }
+
+ /**
+ * Sets the destination of the booking.
+ *
+ * @param withDestination The new destination.
+ */
+ public void setDestination(String withDestination) {
+ this.destination = withDestination;
+ }
+
+ /**
+ * Gets the origin of the booking.
+ *
+ * @return The origin.
+ */
+ public String getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Sets the origin of the booking.
+ *
+ * @param withOrigin The new origin.
+ */
+ public void setOrigin(String withOrigin) {
+ this.origin = withOrigin;
+ }
+
+ /**
+ * Gets the travel date of the booking.
+ *
+ * @return The travel date.
+ */
+ public String getTravelDate() {
+ return travelDate;
+ }
+
+ /**
+ * Sets the travel date of the booking.
+ *
+ * @param withTravelDate The new travel date.
+ */
+ public void setTravelDate(String withTravelDate) {
+ this.travelDate = withTravelDate;
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/BookingDialog.java b/generators/generators/app/templates/core/src/main/java/BookingDialog.java
new file mode 100644
index 000000000..d5e7322b3
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/BookingDialog.java
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.dialogs.DialogTurnResult;
+import com.microsoft.bot.dialogs.WaterfallDialog;
+import com.microsoft.bot.dialogs.WaterfallStep;
+import com.microsoft.bot.dialogs.WaterfallStepContext;
+import com.microsoft.bot.dialogs.prompts.ConfirmPrompt;
+import com.microsoft.bot.dialogs.prompts.PromptOptions;
+import com.microsoft.bot.dialogs.prompts.TextPrompt;
+import com.microsoft.bot.schema.Activity;
+import com.microsoft.bot.schema.InputHints;
+import com.microsoft.recognizers.datatypes.timex.expression.Constants;
+import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
+
+/**
+ * The class containing the booking dialogs.
+ */
+public class BookingDialog extends CancelAndHelpDialog {
+
+ private final String destinationStepMsgText = "Where would you like to travel to?";
+ private final String originStepMsgText = "Where are you traveling from?";
+
+ /**
+ * The constructor of the Booking Dialog class.
+ */
+ public BookingDialog() {
+ super("BookingDialog");
+
+ addDialog(new TextPrompt("TextPrompt"));
+ addDialog(new ConfirmPrompt("ConfirmPrompt"));
+ addDialog(new DateResolverDialog(null));
+ WaterfallStep[] waterfallSteps = {
+ this::destinationStep,
+ this::originStep,
+ this::travelDateStep,
+ this::confirmStep,
+ this::finalStep
+ };
+ addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
+
+ // The initial child Dialog to run.
+ setInitialDialogId("WaterfallDialog");
+ }
+
+ private CompletableFuture destinationStep(WaterfallStepContext stepContext) {
+ BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
+
+ if (bookingDetails.getDestination() == null || bookingDetails.getDestination().trim().isEmpty()) {
+ Activity promptMessage =
+ MessageFactory.text(destinationStepMsgText, destinationStepMsgText,
+ InputHints.EXPECTING_INPUT
+ );
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(promptMessage);
+ return stepContext.prompt("TextPrompt", promptOptions);
+ }
+
+ return stepContext.next(bookingDetails.getDestination());
+ }
+
+ private CompletableFuture originStep(WaterfallStepContext stepContext) {
+ BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
+
+ bookingDetails.setDestination((String) stepContext.getResult());
+
+ if (bookingDetails.getOrigin() == null || bookingDetails.getOrigin().trim().isEmpty()) {
+ Activity promptMessage =
+ MessageFactory
+ .text(originStepMsgText, originStepMsgText, InputHints.EXPECTING_INPUT);
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(promptMessage);
+ return stepContext.prompt("TextPrompt", promptOptions);
+ }
+
+ return stepContext.next(bookingDetails.getOrigin());
+ }
+
+ private CompletableFuture travelDateStep(WaterfallStepContext stepContext) {
+ BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
+
+ bookingDetails.setOrigin((String) stepContext.getResult());
+
+ if (bookingDetails.getTravelDate() == null || isAmbiguous(bookingDetails.getTravelDate())) {
+ return stepContext.beginDialog("DateResolverDialog", bookingDetails.getTravelDate());
+ }
+
+ return stepContext.next(bookingDetails.getTravelDate());
+ }
+
+ private CompletableFuture confirmStep(WaterfallStepContext stepContext) {
+ BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
+
+ bookingDetails.setTravelDate((String) stepContext.getResult());
+
+ String messageText = String.format(
+ "Please confirm, I have you traveling to: %s from: %s on: %s. Is this correct?",
+ bookingDetails.getDestination(), bookingDetails.getOrigin(), bookingDetails.getTravelDate());
+ Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT);
+
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(promptMessage);
+
+ return stepContext.prompt("ConfirmPrompt", promptOptions);
+ }
+
+
+ private CompletableFuture finalStep(WaterfallStepContext stepContext) {
+ if ((Boolean) stepContext.getResult()) {
+ BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
+ return stepContext.endDialog(bookingDetails);
+ }
+
+ return stepContext.endDialog(null);
+ }
+
+ private static boolean isAmbiguous(String timex) {
+ TimexProperty timexProperty = new TimexProperty(timex);
+ return !timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE);
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java b/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java
new file mode 100644
index 000000000..209b9b3b0
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java
@@ -0,0 +1,80 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.dialogs.ComponentDialog;
+import com.microsoft.bot.dialogs.DialogContext;
+import com.microsoft.bot.dialogs.DialogTurnResult;
+import com.microsoft.bot.dialogs.DialogTurnStatus;
+import com.microsoft.bot.schema.Activity;
+import com.microsoft.bot.schema.ActivityTypes;
+import com.microsoft.bot.schema.InputHints;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * The class in charge of the dialog interruptions.
+ */
+public class CancelAndHelpDialog extends ComponentDialog {
+
+ private final String helpMsgText = "Show help here";
+ private final String cancelMsgText = "Cancelling...";
+
+ /**
+ * The constructor of the CancelAndHelpDialog class.
+ *
+ * @param id The dialog's Id.
+ */
+ public CancelAndHelpDialog(String id) {
+ super(id);
+ }
+
+ /**
+ * Called when the dialog is _continued_, where it is the active dialog and the user replies
+ * with a new activity.
+ *
+ * @param innerDc innerDc The inner {@link DialogContext} for the current turn of conversation.
+ * @return A {@link CompletableFuture} representing the asynchronous operation. If the task is
+ * successful, the result indicates whether the dialog is still active after the turn has been
+ * processed by the dialog. The result may also contain a return value.
+ */
+ @Override
+ protected CompletableFuture onContinueDialog(DialogContext innerDc) {
+ return interrupt(innerDc).thenCompose(result -> {
+ if (result != null) {
+ return CompletableFuture.completedFuture(result);
+ }
+ return super.onContinueDialog(innerDc);
+ });
+ }
+
+ private CompletableFuture interrupt(DialogContext innerDc) {
+ if (innerDc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) {
+ String text = innerDc.getContext().getActivity().getText().toLowerCase();
+
+ switch (text) {
+ case "help":
+ case "?":
+ Activity helpMessage = MessageFactory
+ .text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
+ return innerDc.getContext().sendActivity(helpMessage)
+ .thenCompose(sendResult ->
+ CompletableFuture
+ .completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
+ case "cancel":
+ case "quit":
+ Activity cancelMessage = MessageFactory
+ .text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT);
+ return innerDc.getContext()
+ .sendActivity(cancelMessage)
+ .thenCompose(sendResult -> innerDc.cancelAllDialogs());
+ default:
+ break;
+ }
+ }
+
+ return CompletableFuture.completedFuture(null);
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java b/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java
new file mode 100644
index 000000000..687eb45ac
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java
@@ -0,0 +1,109 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.dialogs.DialogTurnResult;
+import com.microsoft.bot.dialogs.WaterfallDialog;
+import com.microsoft.bot.dialogs.WaterfallStep;
+import com.microsoft.bot.dialogs.WaterfallStepContext;
+import com.microsoft.bot.dialogs.prompts.DateTimePrompt;
+import com.microsoft.bot.dialogs.prompts.DateTimeResolution;
+import com.microsoft.bot.dialogs.prompts.PromptOptions;
+import com.microsoft.bot.dialogs.prompts.PromptValidatorContext;
+import com.microsoft.bot.schema.Activity;
+import com.microsoft.bot.schema.InputHints;
+import com.microsoft.recognizers.datatypes.timex.expression.Constants;
+import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * The class containing the date resolver dialogs.
+ */
+public class DateResolverDialog extends CancelAndHelpDialog {
+ private final String promptMsgText = "When would you like to travel?";
+ private final String repromptMsgText =
+ "I'm sorry, to make your booking please enter a full travel date including Day Month and Year.";
+
+
+ /**
+ * The constructor of the DateResolverDialog class.
+ * @param id The dialog's id.
+ */
+ public DateResolverDialog(@Nullable String id) {
+ super(id != null ? id : "DateResolverDialog");
+
+
+ addDialog(new DateTimePrompt("DateTimePrompt",
+ DateResolverDialog::dateTimePromptValidator, null));
+ WaterfallStep[] waterfallSteps = {
+ this::initialStep,
+ this::finalStep
+ };
+ addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
+
+ // The initial child Dialog to run.
+ setInitialDialogId("WaterfallDialog");
+ }
+
+ private CompletableFuture initialStep(WaterfallStepContext stepContext) {
+ String timex = (String) stepContext.getOptions();
+
+ Activity promptMessage = MessageFactory.text(promptMsgText, promptMsgText, InputHints.EXPECTING_INPUT);
+ Activity repromptMessage = MessageFactory.text(repromptMsgText, repromptMsgText, InputHints.EXPECTING_INPUT);
+
+ if (timex == null) {
+ // We were not given any date at all so prompt the user.
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(promptMessage);
+ promptOptions.setRetryPrompt(repromptMessage);
+ return stepContext.prompt("DateTimePrompt", promptOptions);
+ }
+
+ // We have a Date we just need to check it is unambiguous.
+ TimexProperty timexProperty = new TimexProperty(timex);
+ if (!timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE)) {
+ // This is essentially a "reprompt" of the data we were given up front.
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(repromptMessage);
+ return stepContext.prompt("DateTimePrompt", promptOptions);
+ }
+
+ DateTimeResolution dateTimeResolution = new DateTimeResolution();
+ dateTimeResolution.setTimex(timex);
+ List dateTimeResolutions = new ArrayList();
+ dateTimeResolutions.add(dateTimeResolution);
+ return stepContext.next(dateTimeResolutions);
+ }
+
+ private CompletableFuture finalStep(WaterfallStepContext stepContext) {
+ String timex = ((ArrayList) stepContext.getResult()).get(0).getTimex();
+ return stepContext.endDialog(timex);
+ }
+
+ private static CompletableFuture dateTimePromptValidator(
+ PromptValidatorContext> promptContext
+ ) {
+ if (promptContext.getRecognized().getSucceeded()) {
+ // This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the
+ // Time part. TIMEX is a format that represents DateTime expressions that include some ambiguity.
+ // e.g. missing a Year.
+ String timex = ((List) promptContext.getRecognized().getValue())
+ .get(0).getTimex().split("T")[0];
+
+ // If this is a definite Date including year, month and day we are good otherwise reprompt.
+ // A better solution might be to let the user know what part is actually missing.
+ Boolean isDefinite = new TimexProperty(timex).getTypes().contains(Constants.TimexTypes.DEFINITE);
+
+ return CompletableFuture.completedFuture(isDefinite);
+ }
+
+ return CompletableFuture.completedFuture(false);
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java b/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java
new file mode 100644
index 000000000..13143ced2
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java
@@ -0,0 +1,99 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.codepoetics.protonpack.collectors.CompletableFutures;
+import com.microsoft.bot.builder.ConversationState;
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.builder.UserState;
+import com.microsoft.bot.dialogs.Dialog;
+import com.microsoft.bot.schema.Activity;
+import com.microsoft.bot.schema.Attachment;
+import com.microsoft.bot.schema.ChannelAccount;
+import com.microsoft.bot.schema.Serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * The class containing the welcome dialog.
+ *
+ * @param is a Dialog.
+ */
+public class DialogAndWelcomeBot extends DialogBot {
+
+ /**
+ * Creates a DialogBot.
+ *
+ * @param withConversationState ConversationState to use in the bot
+ * @param withUserState UserState to use
+ * @param withDialog Param inheriting from Dialog class
+ */
+ public DialogAndWelcomeBot(
+ ConversationState withConversationState, UserState withUserState, T withDialog
+ ) {
+ super(withConversationState, withUserState, withDialog);
+ }
+
+ /**
+ * When the {@link #onConversationUpdateActivity(TurnContext)} method receives a conversation
+ * update activity that indicates one or more users other than the bot are joining the
+ * conversation, it calls this method.
+ *
+ * @param membersAdded A list of all the members added to the conversation, as described by the
+ * conversation update activity
+ * @param turnContext The context object for this turn.
+ * @return A task that represents the work queued to execute.
+ */
+ @Override
+ protected CompletableFuture onMembersAdded(
+ List membersAdded, TurnContext turnContext
+ ) {
+ return turnContext.getActivity().getMembersAdded().stream()
+ .filter(member -> !StringUtils
+ .equals(member.getId(), turnContext.getActivity().getRecipient().getId()))
+ .map(channel -> {
+ // Greet anyone that was not the target (recipient) of this message.
+ // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
+ Attachment welcomeCard = createAdaptiveCardAttachment();
+ Activity response = MessageFactory
+ .attachment(welcomeCard, null, "Welcome to Bot Framework!", null);
+
+ return turnContext.sendActivity(response).thenApply(sendResult -> {
+ return Dialog.run(getDialog(), turnContext,
+ getConversationState().createProperty("DialogState")
+ );
+ });
+ })
+ .collect(CompletableFutures.toFutureList())
+ .thenApply(resourceResponse -> null);
+ }
+
+ // Load attachment from embedded resource.
+ private Attachment createAdaptiveCardAttachment() {
+ try (
+ InputStream inputStream = Thread.currentThread().
+ getContextClassLoader().getResourceAsStream("cards/welcomeCard.json")
+ ) {
+ String adaptiveCardJson = IOUtils
+ .toString(inputStream, StandardCharsets.UTF_8.toString());
+
+ Attachment attachment = new Attachment();
+ attachment.setContentType("application/vnd.microsoft.card.adaptive");
+ attachment.setContent(Serialization.jsonToTree(adaptiveCardJson));
+ return attachment;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ return new Attachment();
+ }
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/DialogBot.java b/generators/generators/app/templates/core/src/main/java/DialogBot.java
new file mode 100644
index 000000000..02b74135c
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/DialogBot.java
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.ActivityHandler;
+import com.microsoft.bot.builder.BotState;
+import com.microsoft.bot.builder.ConversationState;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.builder.UserState;
+import com.microsoft.bot.dialogs.Dialog;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * This Bot implementation can run any type of Dialog. The use of type parameterization is to allow
+ * multiple different bots to be run at different endpoints within the same project. This can be
+ * achieved by defining distinct Controller types each with dependency on distinct Bot types. The
+ * ConversationState is used by the Dialog system. The UserState isn't, however, it might have been
+ * used in a Dialog implementation, and the requirement is that all BotState objects are saved at
+ * the end of a turn.
+ *
+ * @param parameter of a type inheriting from Dialog
+ */
+public class DialogBot extends ActivityHandler {
+
+ private Dialog dialog;
+ private BotState conversationState;
+ private BotState userState;
+
+ /**
+ * Gets the dialog in use.
+ *
+ * @return instance of dialog
+ */
+ protected Dialog getDialog() {
+ return dialog;
+ }
+
+ /**
+ * Gets the conversation state.
+ *
+ * @return instance of conversationState
+ */
+ protected BotState getConversationState() {
+ return conversationState;
+ }
+
+ /**
+ * Gets the user state.
+ *
+ * @return instance of userState
+ */
+ protected BotState getUserState() {
+ return userState;
+ }
+
+ /**
+ * Sets the dialog in use.
+ *
+ * @param withDialog the dialog (of Dialog type) to be set
+ */
+ protected void setDialog(Dialog withDialog) {
+ dialog = withDialog;
+ }
+
+ /**
+ * Sets the conversation state.
+ *
+ * @param withConversationState the conversationState (of BotState type) to be set
+ */
+ protected void setConversationState(BotState withConversationState) {
+ conversationState = withConversationState;
+ }
+
+ /**
+ * Sets the user state.
+ *
+ * @param withUserState the userState (of BotState type) to be set
+ */
+ protected void setUserState(BotState withUserState) {
+ userState = withUserState;
+ }
+
+ /**
+ * Creates a DialogBot.
+ *
+ * @param withConversationState ConversationState to use in the bot
+ * @param withUserState UserState to use
+ * @param withDialog Param inheriting from Dialog class
+ */
+ public DialogBot(
+ ConversationState withConversationState, UserState withUserState, T withDialog
+ ) {
+ this.conversationState = withConversationState;
+ this.userState = withUserState;
+ this.dialog = withDialog;
+ }
+
+ /**
+ * Saves the BotState objects at the end of each turn.
+ *
+ * @param turnContext
+ * @return
+ */
+ @Override
+ public CompletableFuture onTurn(TurnContext turnContext) {
+ return super.onTurn(turnContext)
+ .thenCompose(turnResult -> conversationState.saveChanges(turnContext, false))
+ .thenCompose(saveResult -> userState.saveChanges(turnContext, false));
+ }
+
+ /**
+ * This method is executed when the turnContext receives a message activity.
+ *
+ * @param turnContext
+ * @return
+ */
+ @Override
+ protected CompletableFuture onMessageActivity(TurnContext turnContext) {
+ LoggerFactory.getLogger(DialogBot.class).info("Running dialog with Message Activity.");
+
+ // Run the Dialog with the new message Activity.
+ return Dialog.run(dialog, turnContext, conversationState.createProperty("DialogState"));
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java b/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java
new file mode 100644
index 000000000..9d328e1b0
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java
@@ -0,0 +1,153 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.microsoft.bot.ai.luis.LuisApplication;
+import com.microsoft.bot.ai.luis.LuisRecognizer;
+import com.microsoft.bot.ai.luis.LuisRecognizerOptionsV3;
+import com.microsoft.bot.builder.Recognizer;
+import com.microsoft.bot.builder.RecognizerResult;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.integration.Configuration;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * The class in charge of recognizing the booking information.
+ */
+public class FlightBookingRecognizer implements Recognizer {
+
+ private LuisRecognizer recognizer;
+
+ /**
+ * The constructor of the FlightBookingRecognizer class.
+ *
+ * @param configuration The Configuration object to use.
+ */
+ public FlightBookingRecognizer(Configuration configuration) {
+ Boolean luisIsConfigured = StringUtils.isNotBlank(configuration.getProperty("LuisAppId"))
+ && StringUtils.isNotBlank(configuration.getProperty("LuisAPIKey"))
+ && StringUtils.isNotBlank(configuration.getProperty("LuisAPIHostName"));
+ if (luisIsConfigured) {
+ LuisApplication luisApplication = new LuisApplication(
+ configuration.getProperty("LuisAppId"),
+ configuration.getProperty("LuisAPIKey"),
+ String.format("https://%s", configuration.getProperty("LuisAPIHostName"))
+ );
+ // 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
+ LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(luisApplication);
+ recognizerOptions.setIncludeInstanceData(true);
+
+ this.recognizer = new LuisRecognizer(recognizerOptions);
+ }
+ }
+
+ /**
+ * Verify if the recognizer is configured.
+ *
+ * @return True if it's configured, False if it's not.
+ */
+ public Boolean isConfigured() {
+ return this.recognizer != null;
+ }
+
+ /**
+ * Return an object with preformatted LUIS results for the bot's dialogs to consume.
+ *
+ * @param context A {link TurnContext}
+ * @return A {link RecognizerResult}
+ */
+ public CompletableFuture executeLuisQuery(TurnContext context) {
+ // Returns true if luis is configured in the application.properties and initialized.
+ return this.recognizer.recognize(context);
+ }
+
+ /**
+ * Gets the From data from the entities which is part of the result.
+ *
+ * @param result The recognizer result.
+ * @return The object node representing the From data.
+ */
+ public ObjectNode getFromEntities(RecognizerResult result) {
+ String fromValue = "", fromAirportValue = "";
+ if (result.getEntities().get("$instance").get("From") != null) {
+ fromValue = result.getEntities().get("$instance").get("From").get(0).get("text")
+ .asText();
+ }
+ if (!fromValue.isEmpty()
+ && result.getEntities().get("From").get(0).get("Airport") != null) {
+ fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0)
+ .asText();
+ }
+
+ ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
+ ObjectNode entitiesNode = mapper.createObjectNode();
+ entitiesNode.put("from", fromValue);
+ entitiesNode.put("airport", fromAirportValue);
+ return entitiesNode;
+ }
+
+ /**
+ * Gets the To data from the entities which is part of the result.
+ *
+ * @param result The recognizer result.
+ * @return The object node representing the To data.
+ */
+ public ObjectNode getToEntities(RecognizerResult result) {
+ String toValue = "", toAirportValue = "";
+ if (result.getEntities().get("$instance").get("To") != null) {
+ toValue = result.getEntities().get("$instance").get("To").get(0).get("text").asText();
+ }
+ if (!toValue.isEmpty() && result.getEntities().get("To").get(0).get("Airport") != null) {
+ toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0)
+ .asText();
+ }
+
+ ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
+ ObjectNode entitiesNode = mapper.createObjectNode();
+ entitiesNode.put("to", toValue);
+ entitiesNode.put("airport", toAirportValue);
+ return entitiesNode;
+ }
+
+ /**
+ * This value will be a TIMEX. And we are only interested in a Date so grab the first result and
+ * drop the Time part. TIMEX is a format that represents DateTime expressions that include some
+ * ambiguity. e.g. missing a Year.
+ *
+ * @param result A {link RecognizerResult}
+ * @return The Timex value without the Time model
+ */
+ public String getTravelDate(RecognizerResult result) {
+ JsonNode datetimeEntity = result.getEntities().get("datetime");
+ if (datetimeEntity == null || datetimeEntity.get(0) == null) {
+ return null;
+ }
+
+ JsonNode timex = datetimeEntity.get(0).get("timex");
+ if (timex == null || timex.get(0) == null) {
+ return null;
+ }
+
+ String datetime = timex.get(0).asText().split("T")[0];
+ return datetime;
+ }
+
+ /**
+ * Runs an utterance through a recognizer and returns a generic recognizer result.
+ *
+ * @param turnContext Turn context.
+ * @return Analysis of utterance.
+ */
+ @Override
+ public CompletableFuture recognize(TurnContext turnContext) {
+ return this.recognizer.recognize(turnContext);
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/MainDialog.java b/generators/generators/app/templates/core/src/main/java/MainDialog.java
new file mode 100644
index 000000000..7b6cd1d8c
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/MainDialog.java
@@ -0,0 +1,232 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.dialogs.ComponentDialog;
+import com.microsoft.bot.dialogs.DialogTurnResult;
+import com.microsoft.bot.dialogs.WaterfallDialog;
+import com.microsoft.bot.dialogs.WaterfallStep;
+import com.microsoft.bot.dialogs.WaterfallStepContext;
+import com.microsoft.bot.dialogs.prompts.PromptOptions;
+import com.microsoft.bot.dialogs.prompts.TextPrompt;
+import com.microsoft.bot.schema.Activity;
+import com.microsoft.bot.schema.InputHints;
+import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * The class containing the main dialog for the sample.
+ */
+public class MainDialog extends ComponentDialog {
+
+ private final FlightBookingRecognizer luisRecognizer;
+ private final Integer plusDayValue = 7;
+
+ /**
+ * The constructor of the Main Dialog class.
+ *
+ * @param withLuisRecognizer The FlightBookingRecognizer object.
+ * @param bookingDialog The BookingDialog object with booking dialogs.
+ */
+ public MainDialog(FlightBookingRecognizer withLuisRecognizer, BookingDialog bookingDialog) {
+ super("MainDialog");
+
+ luisRecognizer = withLuisRecognizer;
+
+ addDialog(new TextPrompt("TextPrompt"));
+ addDialog(bookingDialog);
+ WaterfallStep[] waterfallSteps = {
+ this::introStep,
+ this::actStep,
+ this::finalStep
+ };
+ addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
+
+ // The initial child Dialog to run.
+ setInitialDialogId("WaterfallDialog");
+ }
+
+ /**
+ * First step in the waterfall dialog. Prompts the user for a command. Currently, this expects a
+ * booking request, like "book me a flight from Paris to Berlin on march 22" Note that the
+ * sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities.
+ *
+ * @param stepContext A {@link WaterfallStepContext}
+ * @return A {@link DialogTurnResult}
+ */
+ private CompletableFuture introStep(WaterfallStepContext stepContext) {
+ if (!luisRecognizer.isConfigured()) {
+ Activity text = MessageFactory.text("NOTE: LUIS is not configured. "
+ + "To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' "
+ + "to the appsettings.json file.", null, InputHints.IGNORING_INPUT);
+ return stepContext.getContext().sendActivity(text)
+ .thenCompose(sendResult -> stepContext.next(null));
+ }
+
+ // Use the text provided in FinalStepAsync or the default if it is the first time.
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy");
+ String weekLaterDate = LocalDateTime.now().plusDays(plusDayValue).format(formatter);
+ String messageText = stepContext.getOptions() != null
+ ? stepContext.getOptions().toString()
+ : String.format("What can I help you with today?\n"
+ + "Say something like \"Book a flight from Paris to Berlin on %s\"", weekLaterDate);
+ Activity promptMessage = MessageFactory
+ .text(messageText, messageText, InputHints.EXPECTING_INPUT);
+ PromptOptions promptOptions = new PromptOptions();
+ promptOptions.setPrompt(promptMessage);
+ return stepContext.prompt("TextPrompt", promptOptions);
+ }
+
+ /**
+ * Second step in the waterfall. This will use LUIS to attempt to extract the origin,
+ * destination and travel dates. Then, it hands off to the bookingDialog child dialog to collect
+ * any remaining details.
+ *
+ * @param stepContext A {@link WaterfallStepContext}
+ * @return A {@link DialogTurnResult}
+ */
+ private CompletableFuture actStep(WaterfallStepContext stepContext) {
+ if (!luisRecognizer.isConfigured()) {
+ // LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
+ return stepContext.beginDialog("BookingDialog", new BookingDetails());
+ }
+
+ // Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
+ return luisRecognizer.recognize(stepContext.getContext()).thenCompose(luisResult -> {
+ switch (luisResult.getTopScoringIntent().intent) {
+ case "BookFlight":
+ // Extract the values for the composite entities from the LUIS result.
+ ObjectNode fromEntities = luisRecognizer.getFromEntities(luisResult);
+ ObjectNode toEntities = luisRecognizer.getToEntities(luisResult);
+
+ // Show a warning for Origin and Destination if we can't resolve them.
+ return showWarningForUnsupportedCities(
+ stepContext.getContext(), fromEntities, toEntities)
+ .thenCompose(showResult -> {
+ // Initialize BookingDetails with any entities we may have found in the response.
+
+ BookingDetails bookingDetails = new BookingDetails();
+ bookingDetails.setDestination(toEntities.get("airport").asText());
+ bookingDetails.setOrigin(fromEntities.get("airport").asText());
+ bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
+ // Run the BookingDialog giving it whatever details we have from the LUIS call,
+ // it will fill out the remainder.
+ return stepContext.beginDialog("BookingDialog", bookingDetails);
+ }
+ );
+ case "GetWeather":
+ // We haven't implemented the GetWeatherDialog so we just display a TODO message.
+ String getWeatherMessageText = "TODO: get weather flow here";
+ Activity getWeatherMessage = MessageFactory
+ .text(
+ getWeatherMessageText, getWeatherMessageText,
+ InputHints.IGNORING_INPUT
+ );
+ return stepContext.getContext().sendActivity(getWeatherMessage)
+ .thenCompose(resourceResponse -> stepContext.next(null));
+
+ default:
+ // Catch all for unhandled intents
+ String didntUnderstandMessageText = String.format(
+ "Sorry, I didn't get that. Please "
+ + " try asking in a different way (intent was %s)",
+ luisResult.getTopScoringIntent().intent
+ );
+ Activity didntUnderstandMessage = MessageFactory
+ .text(
+ didntUnderstandMessageText, didntUnderstandMessageText,
+ InputHints.IGNORING_INPUT
+ );
+ return stepContext.getContext().sendActivity(didntUnderstandMessage)
+ .thenCompose(resourceResponse -> stepContext.next(null));
+ }
+ });
+ }
+
+ /**
+ * Shows a warning if the requested From or To cities are recognized as entities but they are
+ * not in the Airport entity list. In some cases LUIS will recognize the From and To composite
+ * entities as a valid cities but the From and To Airport values will be empty if those entity
+ * values can't be mapped to a canonical item in the Airport.
+ *
+ * @param turnContext A {@link WaterfallStepContext}
+ * @param fromEntities An ObjectNode with the entities of From object
+ * @param toEntities An ObjectNode with the entities of To object
+ * @return A task
+ */
+ private static CompletableFuture showWarningForUnsupportedCities(
+ TurnContext turnContext,
+ ObjectNode fromEntities,
+ ObjectNode toEntities
+ ) {
+ List unsupportedCities = new ArrayList();
+
+ if (StringUtils.isNotBlank(fromEntities.get("from").asText())
+ && StringUtils.isBlank(fromEntities.get("airport").asText())) {
+ unsupportedCities.add(fromEntities.get("from").asText());
+ }
+
+ if (StringUtils.isNotBlank(toEntities.get("to").asText())
+ && StringUtils.isBlank(toEntities.get("airport").asText())) {
+ unsupportedCities.add(toEntities.get("to").asText());
+ }
+
+ if (!unsupportedCities.isEmpty()) {
+ String messageText = String.format(
+ "Sorry but the following airports are not supported: %s",
+ String.join(", ", unsupportedCities)
+ );
+ Activity message = MessageFactory
+ .text(messageText, messageText, InputHints.IGNORING_INPUT);
+ return turnContext.sendActivity(message)
+ .thenApply(sendResult -> null);
+ }
+
+ return CompletableFuture.completedFuture(null);
+ }
+
+ /**
+ * This is the final step in the main waterfall dialog. It wraps up the sample "book a flight"
+ * interaction with a simple confirmation.
+ *
+ * @param stepContext A {@link WaterfallStepContext}
+ * @return A {@link DialogTurnResult}
+ */
+ private CompletableFuture finalStep(WaterfallStepContext stepContext) {
+ CompletableFuture stepResult = CompletableFuture.completedFuture(null);
+
+ // If the child dialog ("BookingDialog") was cancelled,
+ // the user failed to confirm or if the intent wasn't BookFlight
+ // the Result here will be null.
+ if (stepContext.getResult() instanceof BookingDetails) {
+ // Now we have all the booking details call the booking service.
+ // If the call to the booking service was successful tell the user.
+ BookingDetails result = (BookingDetails) stepContext.getResult();
+ TimexProperty timeProperty = new TimexProperty(result.getTravelDate());
+ String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now());
+ String messageText = String.format("I have you booked to %s from %s on %s",
+ result.getDestination(), result.getOrigin(), travelDateMsg
+ );
+ Activity message = MessageFactory
+ .text(messageText, messageText, InputHints.IGNORING_INPUT);
+ stepResult = stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
+ }
+
+ // Restart the main dialog with a different message the second time around
+ String promptMessage = "What else can I do for you?";
+ return stepResult
+ .thenCompose(result -> stepContext.replaceDialog(getInitialDialogId(), promptMessage));
+ }
+}
diff --git a/generators/generators/app/templates/core/src/main/java/package-info.java b/generators/generators/app/templates/core/src/main/java/package-info.java
new file mode 100644
index 000000000..1a5f44b0c
--- /dev/null
+++ b/generators/generators/app/templates/core/src/main/java/package-info.java
@@ -0,0 +1,8 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for
+// license information.
+
+/**
+ * This package contains the classes for the core-bot sample.
+ */
+package <%= packageName %>;
diff --git a/generators/generators/app/templates/core/src/test/java/ApplicationTest.java b/generators/generators/app/templates/core/src/test/java/ApplicationTest.java
new file mode 100644
index 000000000..9b84031d4
--- /dev/null
+++ b/generators/generators/app/templates/core/src/test/java/ApplicationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class ApplicationTest {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/generators/generators/app/templates/echo/project/README.md b/generators/generators/app/templates/echo/project/README.md
new file mode 100644
index 000000000..0d514610a
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/README.md
@@ -0,0 +1,85 @@
+# <%= botName %>
+
+This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back.
+
+This sample is a Spring Boot app and uses the Azure CLI and azure-webapp Maven plugin to deploy to Azure.
+
+## Prerequisites
+
+- Java 1.8+
+- Install [Maven](https://maven.apache.org/)
+- An account on [Azure](https://azure.microsoft.com) if you want to deploy to Azure.
+
+## To try this sample locally
+- From the root of this project folder:
+ - Build the sample using `mvn package`
+ - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar`
+
+- Test the bot using Bot Framework Emulator
+
+ [Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
+
+ - Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
+
+ - Connect to the bot using Bot Framework Emulator
+
+ - Launch Bot Framework Emulator
+ - File -> Open Bot
+ - Enter a Bot URL of `http://localhost:3978/api/messages`
+
+## Deploy the bot to Azure
+
+As described on [Deploy your bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli), you will perform the first 4 steps to setup the Azure app, then deploy the code using the azure-webapp Maven plugin.
+
+### 1. Login to Azure
+From a command (or PowerShell) prompt in the root of the bot folder, execute:
+`az login`
+
+### 2. Set the subscription
+`az account set --subscription ""`
+
+If you aren't 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.
+
+### 3. Create an App registration
+`az ad app create --display-name "" --password "" --available-to-other-tenants`
+
+Replace `` and `` with your own values.
+
+`` is the unique name of your bot.
+`` is a minimum 16 character password for your bot.
+
+Record the `appid` from the returned JSON
+
+### 4. Create the Azure resources
+Replace the values for ``, ``, ``, and `` in the following commands:
+
+#### To a new Resource Group
+`az deployment sub create --name "echoBotDeploy" --location "westus" --template-file ".\deploymentTemplates\template-with-new-rg.json" --parameters appId="" appSecret="" botId="" botSku=S1 newAppServicePlanName="echoBotPlan" newWebAppName="echoBot" groupLocation="westus" newAppServicePlanLocation="westus"`
+
+#### To an existing Resource Group
+`az deployment group create --resource-group "" --template-file ".\deploymentTemplates\template-with-preexisting-rg.json" --parameters appId="" appSecret="" botId="" newWebAppName="echoBot" newAppServicePlanName="echoBotPlan" appServicePlanLocation="westus" --name "echoBot"`
+
+### 5. Update app id and password
+In src/main/resources/application.properties update
+- `MicrosoftAppPassword` with the botsecret value
+- `MicrosoftAppId` with the appid from the first step
+
+### 6. Deploy the code
+- Execute `mvn clean package`
+- Execute `mvn azure-webapp:deploy -Dgroupname="" -Dbotname=""`
+
+If the deployment is successful, you will be able to test it via "Test in Web Chat" from the Azure Portal using the "Bot Channel Registration" for the bot.
+
+After the bot is deployed, you only need to execute #6 if you make changes to the bot.
+
+
+## Further reading
+
+- [Maven Plugin for Azure App Service](https://docs.microsoft.com/en-us/java/api/overview/azure/maven/azure-webapp-maven-plugin/readme?view=azure-java-stable)
+- [Spring Boot](https://spring.io/projects/spring-boot)
+- [Azure for Java cloud developers](https://docs.microsoft.com/en-us/azure/java/?view=azure-java-stable)
+- [Bot Framework Documentation](https://docs.botframework.com)
+- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
+- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
+- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
+- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
diff --git a/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json
new file mode 100644
index 000000000..196cfb933
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json
@@ -0,0 +1,292 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "groupLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the location of the Resource Group."
+ }
+ },
+ "groupName": {
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the name of the Resource Group."
+ }
+ },
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The name of the App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "newAppServicePlanLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The location of the App Service Plan. Defaults to \"westus\"."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "appServicePlanName": "[parameters('newAppServicePlanName')]",
+ "resourcesLocation": "[parameters('newAppServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
+ },
+ "resources": [
+ {
+ "name": "[parameters('groupName')]",
+ "type": "Microsoft.Resources/resourceGroups",
+ "apiVersion": "2018-05-01",
+ "location": "[parameters('groupLocation')]",
+ "properties": {}
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2018-05-01",
+ "name": "storageDeployment",
+ "resourceGroup": "[parameters('groupName')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
+ ],
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "name": "[variables('appServicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[variables('appServicePlanName')]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ],
+ "outputs": {}
+ }
+ }
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json
new file mode 100644
index 000000000..d6feb0a0f
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json
@@ -0,0 +1,260 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The name of the new App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "appServicePlanLocation": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The location of the App Service Plan."
+ }
+ },
+ "existingAppServicePlan": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Name of the existing App Service Plan used to create the Web App for the bot."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
+ "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
+ "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourcesLocation": "[parameters('appServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
+ },
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "condition": "[not(variables('useExistingAppServicePlan'))]",
+ "name": "[variables('servicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/echo/project/pom.xml b/generators/generators/app/templates/echo/project/pom.xml
new file mode 100644
index 000000000..2e9fe1986
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/pom.xml
@@ -0,0 +1,259 @@
+
+
+
+ 4.0.0
+
+ <%= packageName %>
+ <%= artifact %>
+ 1.0.0
+ jar
+
+ ${project.groupId}:${project.artifactId}
+ This package contains a Java Bot Echo.
+ http://maven.apache.org
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.0
+
+
+
+
+ 1.8
+ 1.8
+ 1.8
+ <%= packageName %>.Application
+ https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/
+
+
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.4.0
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+ 2.15.0
+ test
+
+
+
+ com.microsoft.bot
+ bot-integration-spring
+ 4.14.1
+ compile
+
+
+
+
+
+ MyGet
+ ${repo.url}
+
+
+
+
+
+ ossrh
+
+ https://oss.sonatype.org/
+
+
+
+
+
+
+ build
+
+ true
+
+
+
+
+ src/main/resources
+ false
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+
+
+
+ maven-war-plugin
+ 3.2.3
+
+ src/main/webapp
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+ <%= packageName %>.Application
+
+
+
+
+
+ com.microsoft.azure
+ azure-webapp-maven-plugin
+ 1.7.0
+
+ V2
+ ${groupname}
+ ${botname}
+
+
+ JAVA_OPTS
+ -Dserver.port=80
+
+
+
+ linux
+ jre8
+ jre8
+
+
+
+
+ ${project.basedir}/target
+
+ *.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 3.7.1
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
+
+
+ publish
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+ true
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/echo/project/src/main/resources/application.properties b/generators/generators/app/templates/echo/project/src/main/resources/application.properties
new file mode 100644
index 000000000..d7d0ee864
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+MicrosoftAppId=
+MicrosoftAppPassword=
+server.port=3978
diff --git a/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json b/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json
new file mode 100644
index 000000000..ad838e77f
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json
@@ -0,0 +1,21 @@
+{
+ "configuration": {
+ "name": "Default",
+ "appenders": {
+ "Console": {
+ "name": "Console-Appender",
+ "target": "SYSTEM_OUT",
+ "PatternLayout": {
+ "pattern":
+ "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
+ }
+ }
+ },
+ "loggers": {
+ "root": {
+ "level": "debug",
+ "appender-ref": { "ref": "Console-Appender", "level": "debug" }
+ }
+ }
+ }
+}
diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..254272e1c
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..383c19004
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,12 @@
+
+
+ dispatcher
+
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ /WEB-INF/spring/dispatcher-config.xml
+
+ 1
+
\ No newline at end of file
diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/index.html b/generators/generators/app/templates/echo/project/src/main/webapp/index.html
new file mode 100644
index 000000000..1b10a9051
--- /dev/null
+++ b/generators/generators/app/templates/echo/project/src/main/webapp/index.html
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+ <%= botName %>
+
+
+
+
+
+
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
Visit Azure
+ Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks
+ like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/echo/src/main/java/Application.java b/generators/generators/app/templates/echo/src/main/java/Application.java
new file mode 100644
index 000000000..001ead194
--- /dev/null
+++ b/generators/generators/app/templates/echo/src/main/java/Application.java
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.Bot;
+import com.microsoft.bot.integration.AdapterWithErrorHandler;
+import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
+import com.microsoft.bot.integration.Configuration;
+import com.microsoft.bot.integration.spring.BotController;
+import com.microsoft.bot.integration.spring.BotDependencyConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+
+//
+// This is the starting point of the Sprint Boot Bot application.
+//
+@SpringBootApplication
+
+// Use the default BotController to receive incoming Channel messages. A custom
+// controller could be used by eliminating this import and creating a new
+// org.springframework.web.bind.annotation.RestController.
+// The default controller is created by the Spring Boot container using
+// dependency injection. The default route is /api/messages.
+@Import({BotController.class})
+
+/**
+ * This class extends the BotDependencyConfiguration which provides the default
+ * implementations for a Bot application. The Application class should
+ * override methods in order to provide custom implementations.
+ */
+public class Application extends BotDependencyConfiguration {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ /**
+ * Returns the Bot for this application.
+ *
+ *
+ * The @Component annotation could be used on the Bot class instead of this method
+ * with the @Bean annotation.
+ *
+ *
+ * @return The Bot implementation for this application.
+ */
+ @Bean
+ public Bot getBot() {
+ return new EchoBot();
+ }
+
+ /**
+ * Returns a custom Adapter that provides error handling.
+ *
+ * @param configuration The Configuration object to use.
+ * @return An error handling BotFrameworkHttpAdapter.
+ */
+ @Override
+ public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) {
+ return new AdapterWithErrorHandler(configuration);
+ }
+}
diff --git a/generators/generators/app/templates/echo/src/main/java/EchoBot.java b/generators/generators/app/templates/echo/src/main/java/EchoBot.java
new file mode 100644
index 000000000..9a474ab44
--- /dev/null
+++ b/generators/generators/app/templates/echo/src/main/java/EchoBot.java
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.codepoetics.protonpack.collectors.CompletableFutures;
+import com.microsoft.bot.builder.ActivityHandler;
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.schema.ChannelAccount;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * This class implements the functionality of the Bot.
+ *
+ *
+ * This is where application specific logic for interacting with the users would be added. For this
+ * sample, the {@link #onMessageActivity(TurnContext)} echos the text back to the user. The {@link
+ * #onMembersAdded(List, TurnContext)} will send a greeting to new conversation participants.
+ *
+ */
+public class EchoBot extends ActivityHandler {
+
+ @Override
+ protected CompletableFuture onMessageActivity(TurnContext turnContext) {
+ return turnContext.sendActivity(
+ MessageFactory.text("Echo: " + turnContext.getActivity().getText())
+ ).thenApply(sendResult -> null);
+ }
+
+ @Override
+ protected CompletableFuture onMembersAdded(
+ List membersAdded,
+ TurnContext turnContext
+ ) {
+ return membersAdded.stream()
+ .filter(
+ member -> !StringUtils
+ .equals(member.getId(), turnContext.getActivity().getRecipient().getId())
+ ).map(channel -> turnContext.sendActivity(MessageFactory.text("Hello and welcome!")))
+ .collect(CompletableFutures.toFutureList()).thenApply(resourceResponses -> null);
+ }
+}
diff --git a/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java b/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java
new file mode 100644
index 000000000..37084390c
--- /dev/null
+++ b/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class ApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/generators/generators/app/templates/empty/project/README.md b/generators/generators/app/templates/empty/project/README.md
new file mode 100644
index 000000000..0d514610a
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/README.md
@@ -0,0 +1,85 @@
+# <%= botName %>
+
+This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back.
+
+This sample is a Spring Boot app and uses the Azure CLI and azure-webapp Maven plugin to deploy to Azure.
+
+## Prerequisites
+
+- Java 1.8+
+- Install [Maven](https://maven.apache.org/)
+- An account on [Azure](https://azure.microsoft.com) if you want to deploy to Azure.
+
+## To try this sample locally
+- From the root of this project folder:
+ - Build the sample using `mvn package`
+ - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar`
+
+- Test the bot using Bot Framework Emulator
+
+ [Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
+
+ - Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
+
+ - Connect to the bot using Bot Framework Emulator
+
+ - Launch Bot Framework Emulator
+ - File -> Open Bot
+ - Enter a Bot URL of `http://localhost:3978/api/messages`
+
+## Deploy the bot to Azure
+
+As described on [Deploy your bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli), you will perform the first 4 steps to setup the Azure app, then deploy the code using the azure-webapp Maven plugin.
+
+### 1. Login to Azure
+From a command (or PowerShell) prompt in the root of the bot folder, execute:
+`az login`
+
+### 2. Set the subscription
+`az account set --subscription ""`
+
+If you aren't 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.
+
+### 3. Create an App registration
+`az ad app create --display-name "" --password "" --available-to-other-tenants`
+
+Replace `` and `` with your own values.
+
+`` is the unique name of your bot.
+`` is a minimum 16 character password for your bot.
+
+Record the `appid` from the returned JSON
+
+### 4. Create the Azure resources
+Replace the values for ``, ``, ``, and `` in the following commands:
+
+#### To a new Resource Group
+`az deployment sub create --name "echoBotDeploy" --location "westus" --template-file ".\deploymentTemplates\template-with-new-rg.json" --parameters appId="" appSecret="" botId="" botSku=S1 newAppServicePlanName="echoBotPlan" newWebAppName="echoBot" groupLocation="westus" newAppServicePlanLocation="westus"`
+
+#### To an existing Resource Group
+`az deployment group create --resource-group "" --template-file ".\deploymentTemplates\template-with-preexisting-rg.json" --parameters appId="" appSecret="" botId="" newWebAppName="echoBot" newAppServicePlanName="echoBotPlan" appServicePlanLocation="westus" --name "echoBot"`
+
+### 5. Update app id and password
+In src/main/resources/application.properties update
+- `MicrosoftAppPassword` with the botsecret value
+- `MicrosoftAppId` with the appid from the first step
+
+### 6. Deploy the code
+- Execute `mvn clean package`
+- Execute `mvn azure-webapp:deploy -Dgroupname="" -Dbotname=""`
+
+If the deployment is successful, you will be able to test it via "Test in Web Chat" from the Azure Portal using the "Bot Channel Registration" for the bot.
+
+After the bot is deployed, you only need to execute #6 if you make changes to the bot.
+
+
+## Further reading
+
+- [Maven Plugin for Azure App Service](https://docs.microsoft.com/en-us/java/api/overview/azure/maven/azure-webapp-maven-plugin/readme?view=azure-java-stable)
+- [Spring Boot](https://spring.io/projects/spring-boot)
+- [Azure for Java cloud developers](https://docs.microsoft.com/en-us/azure/java/?view=azure-java-stable)
+- [Bot Framework Documentation](https://docs.botframework.com)
+- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
+- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
+- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
+- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
diff --git a/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json
new file mode 100644
index 000000000..196cfb933
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json
@@ -0,0 +1,292 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "groupLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the location of the Resource Group."
+ }
+ },
+ "groupName": {
+ "type": "string",
+ "metadata": {
+ "description": "Specifies the name of the Resource Group."
+ }
+ },
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The name of the App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "newAppServicePlanLocation": {
+ "defaultValue": "",
+ "type": "string",
+ "metadata": {
+ "description": "The location of the App Service Plan. Defaults to \"westus\"."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "appServicePlanName": "[parameters('newAppServicePlanName')]",
+ "resourcesLocation": "[parameters('newAppServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
+ },
+ "resources": [
+ {
+ "name": "[parameters('groupName')]",
+ "type": "Microsoft.Resources/resourceGroups",
+ "apiVersion": "2018-05-01",
+ "location": "[parameters('groupLocation')]",
+ "properties": {}
+ },
+ {
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2018-05-01",
+ "name": "storageDeployment",
+ "resourceGroup": "[parameters('groupName')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
+ ],
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "name": "[variables('appServicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[variables('appServicePlanName')]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ],
+ "outputs": {}
+ }
+ }
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json
new file mode 100644
index 000000000..d6feb0a0f
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json
@@ -0,0 +1,260 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "appId": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
+ }
+ },
+ "appSecret": {
+ "type": "string",
+ "metadata": {
+ "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
+ }
+ },
+ "botId": {
+ "type": "string",
+ "metadata": {
+ "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
+ }
+ },
+ "botSku": {
+ "defaultValue": "S1",
+ "type": "string",
+ "metadata": {
+ "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
+ }
+ },
+ "newAppServicePlanName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The name of the new App Service Plan."
+ }
+ },
+ "newAppServicePlanSku": {
+ "type": "object",
+ "defaultValue": {
+ "name": "P1v2",
+ "tier": "PremiumV2",
+ "size": "P1v2",
+ "family": "Pv2",
+ "capacity": 1
+ },
+ "metadata": {
+ "description": "The SKU of the App Service Plan. Defaults to Standard values."
+ }
+ },
+ "appServicePlanLocation": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The location of the App Service Plan."
+ }
+ },
+ "existingAppServicePlan": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Name of the existing App Service Plan used to create the Web App for the bot."
+ }
+ },
+ "newWebAppName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
+ }
+ }
+ },
+ "variables": {
+ "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
+ "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
+ "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
+ "publishingUsername": "[concat('$', parameters('newWebAppName'))]",
+ "resourcesLocation": "[parameters('appServicePlanLocation')]",
+ "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
+ "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
+ "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
+ },
+ "resources": [
+ {
+ "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
+ "type": "Microsoft.Web/serverfarms",
+ "condition": "[not(variables('useExistingAppServicePlan'))]",
+ "name": "[variables('servicePlanName')]",
+ "apiVersion": "2018-02-01",
+ "location": "[variables('resourcesLocation')]",
+ "sku": "[parameters('newAppServicePlanSku')]",
+ "kind": "linux",
+ "properties": {
+ "perSiteScaling": false,
+ "maximumElasticWorkerCount": 1,
+ "isSpot": false,
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "targetWorkerCount": 0,
+ "targetWorkerSizeId": 0
+ }
+ },
+ {
+ "comments": "Create a Web App using a Linux App Service Plan",
+ "type": "Microsoft.Web/sites",
+ "apiVersion": "2018-11-01",
+ "location": "[variables('resourcesLocation')]",
+ "kind": "app,linux",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
+ ],
+ "name": "[variables('webAppName')]",
+ "properties": {
+ "name": "[variables('webAppName')]",
+ "hostNameSslStates": [
+ {
+ "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Standard"
+ },
+ {
+ "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
+ "sslState": "Disabled",
+ "hostType": "Repository"
+ }
+ ],
+ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
+ "reserved": true,
+ "isXenon": false,
+ "hyperV": false,
+ "scmSiteAlsoStopped": false,
+ "clientAffinityEnabled": true,
+ "clientCertEnabled": false,
+ "hostNamesDisabled": false,
+ "containerSize": 0,
+ "dailyMemoryTimeQuota": 0,
+ "httpsOnly": false,
+ "redundancyMode": "None",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "JAVA_OPTS",
+ "value": "-Dserver.port=80"
+ },
+ {
+ "name": "MicrosoftAppId",
+ "value": "[parameters('appId')]"
+ },
+ {
+ "name": "MicrosoftAppPassword",
+ "value": "[parameters('appSecret')]"
+ }
+ ],
+ "cors": {
+ "allowedOrigins": [
+ "https://botservice.hosting.portal.azure.net",
+ "https://hosting.onecloud.azure-test.net/"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "Microsoft.Web/sites/config",
+ "apiVersion": "2018-11-01",
+ "name": "[concat(variables('webAppName'), '/web')]",
+ "location": "[variables('resourcesLocation')]",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ],
+ "properties": {
+ "numberOfWorkers": 1,
+ "defaultDocuments": [
+ "Default.htm",
+ "Default.html",
+ "Default.asp",
+ "index.htm",
+ "index.html",
+ "iisstart.htm",
+ "default.aspx",
+ "index.php",
+ "hostingstart.html"
+ ],
+ "netFrameworkVersion": "v4.0",
+ "linuxFxVersion": "JAVA|8-jre8",
+ "requestTracingEnabled": false,
+ "remoteDebuggingEnabled": false,
+ "httpLoggingEnabled": false,
+ "logsDirectorySizeLimit": 35,
+ "detailedErrorLoggingEnabled": false,
+ "publishingUsername": "[variables('publishingUsername')]",
+ "scmType": "None",
+ "use32BitWorkerProcess": true,
+ "webSocketsEnabled": false,
+ "alwaysOn": true,
+ "managedPipelineMode": "Integrated",
+ "virtualApplications": [
+ {
+ "virtualPath": "/",
+ "physicalPath": "site\\wwwroot",
+ "preloadEnabled": true
+ }
+ ],
+ "loadBalancing": "LeastRequests",
+ "experiments": {
+ "rampUpRules": []
+ },
+ "autoHealEnabled": false,
+ "localMySqlEnabled": false,
+ "ipSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictions": [
+ {
+ "ipAddress": "Any",
+ "action": "Allow",
+ "priority": 1,
+ "name": "Allow all",
+ "description": "Allow all access"
+ }
+ ],
+ "scmIpSecurityRestrictionsUseMain": false,
+ "http20Enabled": false,
+ "minTlsVersion": "1.2",
+ "ftpsState": "AllAllowed",
+ "reservedInstanceCount": 0
+ }
+ },
+ {
+ "apiVersion": "2021-03-01",
+ "type": "Microsoft.BotService/botServices",
+ "name": "[parameters('botId')]",
+ "location": "global",
+ "kind": "azurebot",
+ "sku": {
+ "name": "[parameters('botSku')]"
+ },
+ "properties": {
+ "name": "[parameters('botId')]",
+ "displayName": "[parameters('botId')]",
+ "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
+ "endpoint": "[variables('botEndpoint')]",
+ "msaAppId": "[parameters('appId')]",
+ "luisAppIds": [],
+ "schemaTransformationVersion": "1.3",
+ "isCmekEnabled": false,
+ "isIsolated": false
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
+ ]
+ }
+ ]
+}
diff --git a/generators/generators/app/templates/empty/project/pom.xml b/generators/generators/app/templates/empty/project/pom.xml
new file mode 100644
index 000000000..2e9fe1986
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/pom.xml
@@ -0,0 +1,259 @@
+
+
+
+ 4.0.0
+
+ <%= packageName %>
+ <%= artifact %>
+ 1.0.0
+ jar
+
+ ${project.groupId}:${project.artifactId}
+ This package contains a Java Bot Echo.
+ http://maven.apache.org
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.0
+
+
+
+
+ 1.8
+ 1.8
+ 1.8
+ <%= packageName %>.Application
+ https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/
+
+
+
+
+ junit
+ junit
+ 4.13.1
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 2.4.0
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.17.1
+
+
+ org.apache.logging.log4j
+ log4j-to-slf4j
+ 2.15.0
+ test
+
+
+
+ com.microsoft.bot
+ bot-integration-spring
+ 4.14.1
+ compile
+
+
+
+
+
+ MyGet
+ ${repo.url}
+
+
+
+
+
+ ossrh
+
+ https://oss.sonatype.org/
+
+
+
+
+
+
+ build
+
+ true
+
+
+
+
+ src/main/resources
+ false
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+
+
+
+ maven-war-plugin
+ 3.2.3
+
+ src/main/webapp
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+ <%= packageName %>.Application
+
+
+
+
+
+ com.microsoft.azure
+ azure-webapp-maven-plugin
+ 1.7.0
+
+ V2
+ ${groupname}
+ ${botname}
+
+
+ JAVA_OPTS
+ -Dserver.port=80
+
+
+
+ linux
+ jre8
+ jre8
+
+
+
+
+ ${project.basedir}/target
+
+ *.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 3.7.1
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
+
+
+ publish
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+ true
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/empty/project/src/main/resources/application.properties b/generators/generators/app/templates/empty/project/src/main/resources/application.properties
new file mode 100644
index 000000000..d7d0ee864
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+MicrosoftAppId=
+MicrosoftAppPassword=
+server.port=3978
diff --git a/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json b/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json
new file mode 100644
index 000000000..ad838e77f
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json
@@ -0,0 +1,21 @@
+{
+ "configuration": {
+ "name": "Default",
+ "appenders": {
+ "Console": {
+ "name": "Console-Appender",
+ "target": "SYSTEM_OUT",
+ "PatternLayout": {
+ "pattern":
+ "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"
+ }
+ }
+ },
+ "loggers": {
+ "root": {
+ "level": "debug",
+ "appender-ref": { "ref": "Console-Appender", "level": "debug" }
+ }
+ }
+ }
+}
diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..254272e1c
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..383c19004
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,12 @@
+
+
+ dispatcher
+
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ /WEB-INF/spring/dispatcher-config.xml
+
+ 1
+
\ No newline at end of file
diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/index.html b/generators/generators/app/templates/empty/project/src/main/webapp/index.html
new file mode 100644
index 000000000..1b10a9051
--- /dev/null
+++ b/generators/generators/app/templates/empty/project/src/main/webapp/index.html
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+ <%= botName %>
+
+
+
+
+
+
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
Visit Azure
+ Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks
+ like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+
+
+
+
+
diff --git a/generators/generators/app/templates/empty/src/main/java/Application.java b/generators/generators/app/templates/empty/src/main/java/Application.java
new file mode 100644
index 000000000..44992727f
--- /dev/null
+++ b/generators/generators/app/templates/empty/src/main/java/Application.java
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.microsoft.bot.builder.Bot;
+import com.microsoft.bot.integration.AdapterWithErrorHandler;
+import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
+import com.microsoft.bot.integration.Configuration;
+import com.microsoft.bot.integration.spring.BotController;
+import com.microsoft.bot.integration.spring.BotDependencyConfiguration;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+
+//
+// This is the starting point of the Sprint Boot Bot application.
+//
+@SpringBootApplication
+
+// Use the default BotController to receive incoming Channel messages. A custom
+// controller could be used by eliminating this import and creating a new
+// org.springframework.web.bind.annotation.RestController.
+// The default controller is created by the Spring Boot container using
+// dependency injection. The default route is /api/messages.
+@Import({BotController.class})
+
+/**
+ * This class extends the BotDependencyConfiguration which provides the default
+ * implementations for a Bot application. The Application class should
+ * override methods in order to provide custom implementations.
+ */
+public class Application extends BotDependencyConfiguration {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+ /**
+ * Returns the Bot for this application.
+ *
+ *
+ * The @Component annotation could be used on the Bot class instead of this method
+ * with the @Bean annotation.
+ *
+ *
+ * @return The Bot implementation for this application.
+ */
+ @Bean
+ public Bot getBot() {
+ return new EmptyBot();
+ }
+
+ /**
+ * Returns a custom Adapter that provides error handling.
+ *
+ * @param configuration The Configuration object to use.
+ * @return An error handling BotFrameworkHttpAdapter.
+ */
+ @Override
+ public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) {
+ return new AdapterWithErrorHandler(configuration);
+ }
+}
diff --git a/generators/generators/app/templates/empty/src/main/java/EmptyBot.java b/generators/generators/app/templates/empty/src/main/java/EmptyBot.java
new file mode 100644
index 000000000..3f8d6208d
--- /dev/null
+++ b/generators/generators/app/templates/empty/src/main/java/EmptyBot.java
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package <%= packageName %>;
+
+import com.codepoetics.protonpack.collectors.CompletableFutures;
+import com.microsoft.bot.builder.ActivityHandler;
+import com.microsoft.bot.builder.MessageFactory;
+import com.microsoft.bot.builder.TurnContext;
+import com.microsoft.bot.schema.ChannelAccount;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * This class implements the functionality of the Bot.
+ *
+ *
+ * This is where application specific logic for interacting with the users would be added. For this
+ * sample, the {@link #onMessageActivity(TurnContext)} echos the text back to the user. The {@link
+ * #onMembersAdded(List, TurnContext)} will send a greeting to new conversation participants.
+ *