From 2e8358c3167fc92ecad845c576e0d102734c95e9 Mon Sep 17 00:00:00 2001 From: Emilio Munoz Date: Mon, 13 Apr 2020 02:52:42 -0700 Subject: [PATCH 1/4] 54-Task-Module teams sample --- pom.xml | 1 + samples/54.teams-task-module/LICENSE | 21 + samples/54.teams-task-module/README.md | 76 ++++ .../new-rg-parameters.json | 42 ++ .../preexisting-rg-parameters.json | 39 ++ .../template-with-new-rg.json | 191 ++++++++ .../template-with-preexisting-rg.json | 158 +++++++ samples/54.teams-task-module/pom.xml | 320 ++++++++++++++ .../sample/teamstaskmodule/Application.java | 46 ++ .../teamstaskmodule/TeamsTaskModuleBot.java | 80 ++++ .../src/main/resources/application.properties | 2 + .../src/main/resources/log4j2.json | 18 + .../src/main/webapp/META-INF/MANIFEST.MF | 3 + .../src/main/webapp/WEB-INF/web.xml | 12 + .../src/main/webapp/index.html | 418 ++++++++++++++++++ .../teamstaskmodule/ApplicationTests.java | 19 + .../teamsAppManifest/icon-color.png | Bin 0 -> 1229 bytes .../teamsAppManifest/icon-outline.png | Bin 0 -> 383 bytes .../teamsAppManifest/manifest.json | 66 +++ 19 files changed, 1512 insertions(+) create mode 100644 samples/54.teams-task-module/LICENSE create mode 100644 samples/54.teams-task-module/README.md create mode 100644 samples/54.teams-task-module/deploymentTemplates/new-rg-parameters.json create mode 100644 samples/54.teams-task-module/deploymentTemplates/preexisting-rg-parameters.json create mode 100644 samples/54.teams-task-module/deploymentTemplates/template-with-new-rg.json create mode 100644 samples/54.teams-task-module/deploymentTemplates/template-with-preexisting-rg.json create mode 100644 samples/54.teams-task-module/pom.xml create mode 100644 samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java create mode 100644 samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java create mode 100644 samples/54.teams-task-module/src/main/resources/application.properties create mode 100644 samples/54.teams-task-module/src/main/resources/log4j2.json create mode 100644 samples/54.teams-task-module/src/main/webapp/META-INF/MANIFEST.MF create mode 100644 samples/54.teams-task-module/src/main/webapp/WEB-INF/web.xml create mode 100644 samples/54.teams-task-module/src/main/webapp/index.html create mode 100644 samples/54.teams-task-module/src/test/java/com/microsoft/bot/sample/teamstaskmodule/ApplicationTests.java create mode 100644 samples/54.teams-task-module/teamsAppManifest/icon-color.png create mode 100644 samples/54.teams-task-module/teamsAppManifest/icon-outline.png create mode 100644 samples/54.teams-task-module/teamsAppManifest/manifest.json diff --git a/pom.xml b/pom.xml index 488cdd1a..bbbdd16d 100644 --- a/pom.xml +++ b/pom.xml @@ -379,6 +379,7 @@ samples/51.teams-messaging-extensions-action samples/52.teams-messaging-extensions-search-auth-config samples/53.teams-messaging-extensions-action-preview + samples/54.teams-task-module samples/57.teams-conversation-bot diff --git a/samples/54.teams-task-module/LICENSE b/samples/54.teams-task-module/LICENSE new file mode 100644 index 00000000..21071075 --- /dev/null +++ b/samples/54.teams-task-module/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + 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/samples/54.teams-task-module/README.md b/samples/54.teams-task-module/README.md new file mode 100644 index 00000000..5c35b565 --- /dev/null +++ b/samples/54.teams-task-module/README.md @@ -0,0 +1,76 @@ + +# Teams Conversation Bot + +Bot Framework v4 Conversation Bot sample for Teams. + +This bot has been created using [Bot Framework](https://dev.botframework.com). This sample shows +how to incorporate basic conversational flow into a Teams application. It also illustrates a few of the Teams specific calls you can make from your bot. + +## Prerequisites + +- Microsoft Teams is installed and you have an account +- [ngrok](https://ngrok.com/) or equivalent tunnelling solution + +## To try this sample + +> Note these instructions are for running the sample on your local machine, the tunnelling solution is required because +the Teams service needs to call into the bot. + +1) Clone the repository + + ```bash + git clone https://github.com/Microsoft/botbuilder-java.git + ``` + +1) Run ngrok - point to port 8080 + + ```bash + ngrok http -host-header=rewrite 8080 + ``` + +1) Create [Bot Framework registration resource](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) in Azure + - Use the current `https` URL you were given by running ngrok. Append with the path `/api/messages` used by this sample + - Ensure that you've [enabled the Teams Channel](https://docs.microsoft.com/en-us/azure/bot-service/channel-connect-teams?view=azure-bot-service-4.0) + - __*If you don't have an Azure account*__ you can use this [Bot Framework registration](https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/create-a-bot-for-teams#register-your-web-service-with-the-bot-framework) + +1) Update the `resources/application.properties` configuration for the bot to use the Microsoft App Id and App Password from the Bot Framework registration. (Note the App Password is referred to as the "client secret" in the azure portal and you can always create a new client secret anytime.) + +1) __*This step is specific to Teams.*__ + - **Edit** the `manifest.json` contained in the `teamsAppManifest` folder to replace your Microsoft App Id (that was created when you registered your bot earlier) *everywhere* you see the place holder string `<>` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`) + - **Zip** up the contents of the `teamsAppManifest` folder to create a `manifest.zip` + - **Upload** the `manifest.zip` to Teams (in the Apps view click "Upload a custom app") + +1) From the root of this project folder: + - Build the sample using `mvn package` + - Unless done previously, install the packages in the local cache by using `mvn install` + - Run it by using `java -jar .\target\bot-teams-conversation-sample.jar` + + +## Interacting with the bot + +You can interact with this bot by sending it a message, or selecting a command from the command list. The bot will respond to the following strings. + +1. **Show Welcome** + - **Result:** The bot will send the welcome card for you to interact with + - **Valid Scopes:** personal, group chat, team chat +2. **MentionMe** + - **Result:** The bot will respond to the message and mention the user + - **Valid Scopes:** personal, group chat, team chat +3. **MessageAllMembers** + - **Result:** The bot will send a 1-on-1 message to each member in the current conversation (aka on the conversation's roster). + - **Valid Scopes:** personal, group chat, team chat + +You can select an option from the command list by typing ```@TeamsConversationBot``` into the compose message area and ```What can I do?``` text above the compose area. + +### Avoiding Permission-Related Errors + +You may encounter permission-related errors when sending a proactive message. This can often be mitigated by using `MicrosoftAppCredentials.TrustServiceUrl()`. See [the documentation](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp#avoiding-401-unauthorized-errors) for more information. + +## 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 + +- [How Microsoft Teams bots work](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-basics-teams?view=azure-bot-service-4.0&tabs=javascript) + diff --git a/samples/54.teams-task-module/deploymentTemplates/new-rg-parameters.json b/samples/54.teams-task-module/deploymentTemplates/new-rg-parameters.json new file mode 100644 index 00000000..ead33909 --- /dev/null +++ b/samples/54.teams-task-module/deploymentTemplates/new-rg-parameters.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "groupLocation": { + "value": "" + }, + "groupName": { + "value": "" + }, + "appId": { + "value": "" + }, + "appSecret": { + "value": "" + }, + "botId": { + "value": "" + }, + "botSku": { + "value": "" + }, + "newAppServicePlanName": { + "value": "" + }, + "newAppServicePlanSku": { + "value": { + "name": "S1", + "tier": "Standard", + "size": "S1", + "family": "S", + "capacity": 1 + } + }, + "newAppServicePlanLocation": { + "value": "" + }, + "newWebAppName": { + "value": "" + } + } +} \ No newline at end of file diff --git a/samples/54.teams-task-module/deploymentTemplates/preexisting-rg-parameters.json b/samples/54.teams-task-module/deploymentTemplates/preexisting-rg-parameters.json new file mode 100644 index 00000000..b6f5114f --- /dev/null +++ b/samples/54.teams-task-module/deploymentTemplates/preexisting-rg-parameters.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appId": { + "value": "" + }, + "appSecret": { + "value": "" + }, + "botId": { + "value": "" + }, + "botSku": { + "value": "" + }, + "newAppServicePlanName": { + "value": "" + }, + "newAppServicePlanSku": { + "value": { + "name": "S1", + "tier": "Standard", + "size": "S1", + "family": "S", + "capacity": 1 + } + }, + "appServicePlanLocation": { + "value": "" + }, + "existingAppServicePlan": { + "value": "" + }, + "newWebAppName": { + "value": "" + } + } +} \ No newline at end of file diff --git a/samples/54.teams-task-module/deploymentTemplates/template-with-new-rg.json b/samples/54.teams-task-module/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 00000000..dcd6260a --- /dev/null +++ b/samples/54.teams-task-module/deploymentTemplates/template-with-new-rg.json @@ -0,0 +1,191 @@ +{ + "$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": "F0", + "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": { + "resourcesLocation": "[deployment().location]", + "effectiveGroupLocation": "[if(empty(parameters('groupLocation')), variables('resourcesLocation'), parameters('groupLocation'))]", + "effectivePlanLocation": "[if(empty(parameters('newAppServicePlanLocation')), variables('resourcesLocation'), parameters('newAppServicePlanLocation'))]", + "appServicePlanName": "[if(empty(parameters('newAppServicePlanName')), concat(parameters('botId'), 'ServicePlan'), parameters('newAppServicePlanName'))]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" + }, + "resources": [ + { + "name": "[parameters('groupName')]", + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2018-05-01", + "location": "[variables('effectiveGroupLocation')]", + "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 App Service Plan", + "type": "Microsoft.Web/serverfarms", + "name": "[variables('appServicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('effectivePlanLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "name": "[variables('appServicePlanName')]", + "reserved":true + } + }, + { + "comments": "Create a Web App using the new App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2015-08-01", + "location": "[variables('resourcesLocation')]", + "kind": "app", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "serverFarmId": "[variables('appServicePlanName')]", + "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/" + ] + } + } + } + }, + { + "apiVersion": "2017-12-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "bot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "developerAppInsightsApplicationId": null, + "developerAppInsightKey": null, + "publishingCredentials": null, + "storageResourceId": null + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ], + "outputs": {} + } + } + } + ] +} \ No newline at end of file diff --git a/samples/54.teams-task-module/deploymentTemplates/template-with-preexisting-rg.json b/samples/54.teams-task-module/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 00000000..b790d2bd --- /dev/null +++ b/samples/54.teams-task-module/deploymentTemplates/template-with-preexisting-rg.json @@ -0,0 +1,158 @@ +{ + "$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'), if(empty(parameters('newAppServicePlanName')),concat(parameters('botId'), 'ServicePlan'),parameters('newAppServicePlanName')))]", + "resourcesLocation": "[if(empty(parameters('appServicePlanLocation')), resourceGroup().location, 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 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": { + "name": "[variables('servicePlanName')]", + "reserved":true + } + }, + { + "comments": "Create a Web App using an App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2016-08-01", + "location": "[variables('resourcesLocation')]", + "kind": "app", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "serverFarmId": "[variables('servicePlanName')]", + "siteConfig": { + "linuxFxVersion": "JAVA|8-jre8", + "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/" + ] + } + } + } + }, + { + "apiVersion": "2017-12-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "bot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "developerAppInsightsApplicationId": null, + "developerAppInsightKey": null, + "publishingCredentials": null, + "storageResourceId": null + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ] +} \ No newline at end of file diff --git a/samples/54.teams-task-module/pom.xml b/samples/54.teams-task-module/pom.xml new file mode 100644 index 00000000..04b8fd8f --- /dev/null +++ b/samples/54.teams-task-module/pom.xml @@ -0,0 +1,320 @@ + + + + 4.0.0 + + com.microsoft.bot.sample + bot-teams-task-module + sample + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Teams Task Module sample using Spring Boot. + http://maven.apache.org + + + org.springframework.boot + spring-boot-starter-parent + 2.1.7.RELEASE + + + + + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + + Bot Framework Development + + Microsoft + https://dev.botframework.com/ + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + com.microsoft.bot.sample.teamstaskmodule.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.12 + test + + + org.json + json + 20190722 + + + org.springframework.boot + spring-boot-starter-test + 2.1.8.RELEASE + test + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-api + 2.11.0 + + + org.apache.logging.log4j + log4j-core + 2.11.0 + + + + com.microsoft.bot + bot-integration-spring + 4.0.0-SNAPSHOT + compile + + + + + + MyGet + ${repo.url} + + + + + + ossrh + + https://oss.sonatype.org/ + + + + + + + build + + true + + + + + 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 + + + com.microsoft.bot.sample.teamstaskmodule.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.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + yourcoverallsprojectrepositorytoken + + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + ../../cobertura-report/spring-teamstaskmodule-sample + xml + 256m + + true + + + + + + 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 + + + + + + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.12.0 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + + + checkstyle + + + + + + + + diff --git a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java new file mode 100644 index 00000000..67793bd0 --- /dev/null +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamstaskmodule; + +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.Import; + +/** + * This is the starting point of the Sprint Boot Bot application. + * + * This class also provides overrides for dependency injections. A class that extends the + * {@link com.microsoft.bot.builder.Bot} interface should be annotated with @Component. + * + * @see TeamsTaskModuleBot + */ +@SpringBootApplication + +// Use the default BotController to receive incoming Channel messages. A custom controller +// could be used by eliminating this import and creating a new RestController. The default +// controller is created by the Spring Boot container using dependency injection. The +// default route is /api/messages. +@Import({BotController.class}) + +public class Application extends BotDependencyConfiguration { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * 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/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java new file mode 100644 index 00000000..6d1f345b --- /dev/null +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamstaskmodule; + +import com.codepoetics.protonpack.collectors.CompletableFutures; +import com.microsoft.bot.builder.BotFrameworkAdapter; +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.builder.teams.TeamsActivityHandler; +import com.microsoft.bot.builder.teams.TeamsInfo; +import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials; +import com.microsoft.bot.integration.Configuration; +import com.microsoft.bot.schema.ActionTypes; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.CardAction; +import com.microsoft.bot.schema.teams.TaskModuleAction; +import com.microsoft.bot.schema.ConversationParameters; +import com.microsoft.bot.schema.ConversationReference; +import com.microsoft.bot.schema.HeroCard; +import com.microsoft.bot.schema.Mention; +import com.microsoft.bot.schema.teams.TeamInfo; +import com.microsoft.bot.schema.teams.TeamsChannelAccount; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import java.util.ArrayList; +import com.microsoft.bot.schema.Attachment; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.json.*; + +/** + * 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.

+ */ +@Component +public class TeamsTaskModuleBot extends TeamsActivityHandler { + private String appId; + private String appPassword; + + public TeamsTaskModuleBot(Configuration configuration) { + appId = configuration.getProperty("MicrosoftAppId"); + appPassword = configuration.getProperty("MicrosoftAppPassword"); + } + + @Override + public CompletableFuture onTeamsMembersAdded( + List membersAdded, + TeamInfo teamInfo, + TurnContext turnContext + ) { + return turnContext.sendActivity(MessageFactory.attachment(getTaskModuleHeroCard())) + .thenApply(resourceResponse -> null); + } + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + return turnContext.sendActivity(MessageFactory.attachment(getTaskModuleHeroCard())) + .thenApply(resourceResponse -> null); + } + + private Attachment getTaskModuleHeroCard() + { + HeroCard card = new HeroCard() {{ + setTitle(""); + setSubtitle("Click the buttons below to update this card"); + setButtons(Arrays.asList( + new TaskModuleAction("Adaptive Card", "adaptivecard") + )); + }}; + return card.toAttachment(); + } +} diff --git a/samples/54.teams-task-module/src/main/resources/application.properties b/samples/54.teams-task-module/src/main/resources/application.properties new file mode 100644 index 00000000..a695b3bf --- /dev/null +++ b/samples/54.teams-task-module/src/main/resources/application.properties @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= diff --git a/samples/54.teams-task-module/src/main/resources/log4j2.json b/samples/54.teams-task-module/src/main/resources/log4j2.json new file mode 100644 index 00000000..67c0ad53 --- /dev/null +++ b/samples/54.teams-task-module/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/samples/54.teams-task-module/src/main/webapp/META-INF/MANIFEST.MF b/samples/54.teams-task-module/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000..254272e1 --- /dev/null +++ b/samples/54.teams-task-module/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/54.teams-task-module/src/main/webapp/WEB-INF/web.xml b/samples/54.teams-task-module/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..383c1900 --- /dev/null +++ b/samples/54.teams-task-module/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/samples/54.teams-task-module/src/main/webapp/index.html b/samples/54.teams-task-module/src/main/webapp/index.html new file mode 100644 index 00000000..40b74c00 --- /dev/null +++ b/samples/54.teams-task-module/src/main/webapp/index.html @@ -0,0 +1,418 @@ + + + + + + + EchoBot + + + + + +
+
+
+
Spring Boot Bot
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:8080/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/samples/54.teams-task-module/src/test/java/com/microsoft/bot/sample/teamstaskmodule/ApplicationTests.java b/samples/54.teams-task-module/src/test/java/com/microsoft/bot/sample/teamstaskmodule/ApplicationTests.java new file mode 100644 index 00000000..87a36989 --- /dev/null +++ b/samples/54.teams-task-module/src/test/java/com/microsoft/bot/sample/teamstaskmodule/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamstaskmodule; + +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/samples/54.teams-task-module/teamsAppManifest/icon-color.png b/samples/54.teams-task-module/teamsAppManifest/icon-color.png new file mode 100644 index 0000000000000000000000000000000000000000..48a2de13303e1e8a25f76391f4a34c7c4700fd3d GIT binary patch literal 1229 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCe1|JzX3_D&pSWuFnWfl{x;g|9jrEYf8Vqrkk2Ba|%ol3OT){=#|7ID~|e{ zODQ{kU&ME#@`*-tm%Tukt_gFr+`F?$dx9wg-jad`^gsMn2_%Kh%WH91&SjKq5 zgkdI|!exdOVgw@>>=!Tjnk6q)zV*T8$FdgRFYC{kQ7``NOcl@R(_%_8e5e0E;>v0G zEM9kb)2itgOTSfH7M=b3-S61B?PiazMdwXZwrS)^5UUS#HQjaoua5h_{Gx*_Zz|XK z$tf0mZ&=tpf2!!Q)!A_l&o_$g*|JM$VZa~F^0{x1T{=QFu*x$`=V%~jUW=G`iqqp=lquB-`P{Qjw`=zEu3cMc_x7m2f#9m}uoFBMMQ^+%cOL)F_)N@JZ}Axoxi1y= zeebq`y==e!nl+?cK-PhOec!3%|IupShHrcjW8sSt)F1>NW*{ zW%ljk2)nk%-}+F&?gi=7^$L#VeX3@kp%f{n}fR z`}uZPx$IY~r8R5%gMlrc`jP!L3IloKFoq~sFFH5|cdklX=R08T)}71BhaN8$`AsNf0_ zq>WNhAtCd|-nBlTU=y5zl_vXlXZ~bkuaYENMp>3QSQ_#zuYZ+eQh*OIHRxP~s(}ic zN2J4$u=AQcPt)|>F3zZLsjtP;Tajkugx;NcYED2~JVBlVO>{`uAY?Q4O|AA z=16}CJieK^5P_TKnou!zGR`$!PUC)DqtkO;?!`p!+9v3lP_mu=%Vt3BkoWsq%;FN1sp58w*zfr-z^7tIb*q>!yncCjrzLuOk3N+d&~^Cxd| z>", + "packageName": "com.teams.sample.teamsconversationbot", + "developer": { + "name": "teamsConversationBot", + "websiteUrl": "https://www.microsoft.com", + "privacyUrl": "https://www.teams.com/privacy", + "termsOfUseUrl": "https://www.teams.com/termsofuser" + }, + "icons": { + "outline": "icon-outline.png", + "color": "icon-color.png" + }, + "name": { + "short": "TeamsConversationBot", + "full": "TeamsConversationBot" + }, + "description": { + "short": "TeamsConversationBot", + "full": "TeamsConversationBot" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "<>", + "scopes": [ + "personal", + "groupchat", + "team" + ], + "supportsFiles": false, + "isNotificationOnly": false, + "commandLists": [ + { + "scopes": [ + "personal", + "groupchat", + "team" + ], + "commands": [ + { + "title": "MentionMe", + "description": "Sends message with @mention of the sender" + }, + { + "title": "Show Welcome", + "description": "Shows the welcome card" + }, + { + "title": "MessageAllMembers", + "description": "Send 1 to 1 message to all members of the current conversation" + } + ] + } + ] + } + ], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} From 15fe8addbdcde0c5c8fa17525a6c5a5959fe15f0 Mon Sep 17 00:00:00 2001 From: Emilio Munoz Date: Tue, 14 Apr 2020 03:35:09 -0700 Subject: [PATCH 2/4] Adding adaptive card task response --- .../builder/teams/TeamsActivityHandler.java | 2 +- .../bot/schema/teams/TaskModuleAction.java | 15 ++- .../teams/TaskModuleMessageResponse.java | 6 +- samples/54.teams-task-module/README.md | 19 +--- samples/54.teams-task-module/pom.xml | 19 ++-- .../teamstaskmodule/TeamsTaskModuleBot.java | 102 ++++++++++++++---- .../src/main/resources/adaptivecard.json | 25 +++++ .../teamsAppManifest/manifest.json | 100 +++++++---------- 8 files changed, 176 insertions(+), 112 deletions(-) create mode 100644 samples/54.teams-task-module/src/main/resources/adaptivecard.json diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java index 0dde3358..4096ead8 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java @@ -320,7 +320,7 @@ public class TeamsActivityHandler extends ActivityHandler { return notImplemented(); } - protected CompletableFuture onTeamsTaskModuleSubmit( + protected CompletableFuture onTeamsTaskModuleSubmit( TurnContext turnContext, TaskModuleRequest taskModuleRequest) { diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleAction.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleAction.java index f51fe790..48257792 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleAction.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleAction.java @@ -10,6 +10,8 @@ import com.microsoft.bot.schema.ActionTypes; import com.microsoft.bot.schema.CardAction; import org.slf4j.LoggerFactory; +import java.io.IOException; + /** * Adapter class to represent BotBuilder card action as adaptive card action (in type of Action.Submit). */ @@ -27,7 +29,18 @@ public class TaskModuleAction extends CardAction { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.findAndRegisterModules(); - ObjectNode data = objectMapper.valueToTree(withValue); + ObjectNode data = null; + if (withValue instanceof String) { + + try { + data = objectMapper.readValue((String) withValue, ObjectNode.class); + } catch (IOException e) { + LoggerFactory.getLogger(TaskModuleAction.class).error("TaskModuleAction", e); + } + } else { + data = objectMapper.valueToTree(withValue); + } + data.put("type", "task/fetch"); try { diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleMessageResponse.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleMessageResponse.java index f1bdd221..b95ae518 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleMessageResponse.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/TaskModuleMessageResponse.java @@ -10,13 +10,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; */ public class TaskModuleMessageResponse extends TaskModuleResponseBase { @JsonProperty(value = "value") - private TaskModuleTaskInfo value; + private String value; /** * Gets info teams will display the value of value in a popup message box. * @return The popup info. */ - public TaskModuleTaskInfo getValue() { + public String getValue() { return value; } @@ -24,7 +24,7 @@ public class TaskModuleMessageResponse extends TaskModuleResponseBase { * Sets info teams will display the value of value in a popup message box. * @param withValue The popup info. */ - public void setValue(TaskModuleTaskInfo withValue) { + public void setValue(String withValue) { value = withValue; } } diff --git a/samples/54.teams-task-module/README.md b/samples/54.teams-task-module/README.md index 5c35b565..4b27ecac 100644 --- a/samples/54.teams-task-module/README.md +++ b/samples/54.teams-task-module/README.md @@ -3,8 +3,9 @@ Bot Framework v4 Conversation Bot sample for Teams. -This bot has been created using [Bot Framework](https://dev.botframework.com). This sample shows -how to incorporate basic conversational flow into a Teams application. It also illustrates a few of the Teams specific calls you can make from your bot. +Bot Framework Teams Task Module sample. + +This bot has been created using [Bot Framework](https://dev.botframework.com). It shows how to fetch a Task Module from a Hero Card button and receive input from an Adaptive Card in the Task Module. ## Prerequisites @@ -48,19 +49,9 @@ the Teams service needs to call into the bot. ## Interacting with the bot -You can interact with this bot by sending it a message, or selecting a command from the command list. The bot will respond to the following strings. +> Note this `manifest.json` specified that the bot will be installed in "personal", "team" and "groupchat" scope which is why you immediately entered a one on one chat conversation with the bot. You can at mention the bot in a group chat or in a Channel in the Team you installed it in. Please refer to Teams documentation for more details. -1. **Show Welcome** - - **Result:** The bot will send the welcome card for you to interact with - - **Valid Scopes:** personal, group chat, team chat -2. **MentionMe** - - **Result:** The bot will respond to the message and mention the user - - **Valid Scopes:** personal, group chat, team chat -3. **MessageAllMembers** - - **Result:** The bot will send a 1-on-1 message to each member in the current conversation (aka on the conversation's roster). - - **Valid Scopes:** personal, group chat, team chat - -You can select an option from the command list by typing ```@TeamsConversationBot``` into the compose message area and ```What can I do?``` text above the compose area. +You can interact with this bot by sending it a message. The bot will respond with a Hero Card with a button which will display a Task Module when clicked. The Task Module demonstrates retrieving input from a user through a Text Block and a Submit button. ### Avoiding Permission-Related Errors diff --git a/samples/54.teams-task-module/pom.xml b/samples/54.teams-task-module/pom.xml index 04b8fd8f..49d7fb9e 100644 --- a/samples/54.teams-task-module/pom.xml +++ b/samples/54.teams-task-module/pom.xml @@ -54,18 +54,12 @@ 4.12 test - - org.json - json - 20190722 - org.springframework.boot spring-boot-starter-test 2.1.8.RELEASE test - org.slf4j slf4j-api @@ -80,14 +74,23 @@ log4j-core 2.11.0 - com.microsoft.bot bot-integration-spring 4.0.0-SNAPSHOT compile - + + org.json + json + 20190722 + + + io.adaptivecards + adaptivecards-android + 1.2.8 + + diff --git a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java index 6d1f345b..b53e37d8 100644 --- a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java @@ -3,34 +3,27 @@ package com.microsoft.bot.sample.teamstaskmodule; -import com.codepoetics.protonpack.collectors.CompletableFutures; -import com.microsoft.bot.builder.BotFrameworkAdapter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.bot.builder.MessageFactory; import com.microsoft.bot.builder.TurnContext; import com.microsoft.bot.builder.teams.TeamsActivityHandler; -import com.microsoft.bot.builder.teams.TeamsInfo; -import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials; import com.microsoft.bot.integration.Configuration; -import com.microsoft.bot.schema.ActionTypes; import com.microsoft.bot.schema.Activity; -import com.microsoft.bot.schema.CardAction; -import com.microsoft.bot.schema.teams.TaskModuleAction; -import com.microsoft.bot.schema.ConversationParameters; -import com.microsoft.bot.schema.ConversationReference; -import com.microsoft.bot.schema.HeroCard; -import com.microsoft.bot.schema.Mention; -import com.microsoft.bot.schema.teams.TeamInfo; -import com.microsoft.bot.schema.teams.TeamsChannelAccount; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; -import java.util.ArrayList; import com.microsoft.bot.schema.Attachment; +import com.microsoft.bot.schema.HeroCard; +import com.microsoft.bot.schema.teams.*; +import org.apache.commons.io.IOUtils; +import org.json.JSONObject; +import org.springframework.stereotype.Component; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; -import org.json.*; /** * This class implements the functionality of the Bot. @@ -51,7 +44,7 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { } @Override - public CompletableFuture onTeamsMembersAdded( + protected CompletableFuture onTeamsMembersAdded( List membersAdded, TeamInfo teamInfo, TurnContext turnContext @@ -61,20 +54,83 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { } @Override - protected CompletableFuture onMessageActivity(TurnContext turnContext) { - return turnContext.sendActivity(MessageFactory.attachment(getTaskModuleHeroCard())) + protected CompletableFuture onMessageActivity( + TurnContext turnContext) { + Attachment attachment = getTaskModuleHeroCard(); + return turnContext.sendActivity(MessageFactory.attachment(attachment)) .thenApply(resourceResponse -> null); } + @Override + protected CompletableFuture onTeamsTaskModuleFetch( + TurnContext turnContext, + TaskModuleRequest taskModuleRequest) { + + Activity reply = MessageFactory.text("onTeamsTaskModuleFetch TaskModuleRequest: " ); + + turnContext.sendActivity(reply) + .thenApply(resourceResponse -> null); + + Attachment adaptiveCard = getTaskModuleAdaptiveCard(); + + return CompletableFuture.completedFuture(new TaskModuleResponse(){{ + setTask(new TaskModuleContinueResponse(){{ + setType("continue"); + setValue(new TaskModuleTaskInfo(){{ + setCard(adaptiveCard); + setHeight(200); + setWidth(400); + setTitle("Adaptive Card: Inputs"); + }}); + }}); + }}); + } + + @Override + protected CompletableFuture onTeamsTaskModuleSubmit( + TurnContext turnContext, + TaskModuleRequest taskModuleRequest){ + + Activity reply = MessageFactory.text("onTeamsTaskModuleSubmit TaskModuleRequest: " ); + + turnContext.sendActivity(reply) + .thenApply(resourceResponse -> null); + + return CompletableFuture.completedFuture(new TaskModuleResponse(){{ + setTask(new TaskModuleMessageResponse(){{ + setType("message"); + setValue("Thanks!"); + }}); + }}); + + } + private Attachment getTaskModuleHeroCard() { HeroCard card = new HeroCard() {{ setTitle(""); setSubtitle("Click the buttons below to update this card"); setButtons(Arrays.asList( - new TaskModuleAction("Adaptive Card", "adaptivecard") + new TaskModuleAction("Adaptive Card", new JSONObject().put("data", "adaptivecard").toString()) )); }}; return card.toAttachment(); } + + private Attachment getTaskModuleAdaptiveCard(){ + try { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream("adaptivecard.json"); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + + return new Attachment(){{ + setContentType("application/vnd.microsoft.card.adaptive"); + setContent(new ObjectMapper().readValue((String) result, ObjectNode.class)); + }}; + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return new Attachment(); + } } diff --git a/samples/54.teams-task-module/src/main/resources/adaptivecard.json b/samples/54.teams-task-module/src/main/resources/adaptivecard.json new file mode 100644 index 00000000..d60617fc --- /dev/null +++ b/samples/54.teams-task-module/src/main/resources/adaptivecard.json @@ -0,0 +1,25 @@ +{ + "version": "1.0", + "type": "AdaptiveCard", + "body": [ + { + "type": "TextBlock", + "text": "Enter Text Here", + "weight": "bolder", + "isSubtle": false + }, + { + "type": "Input.Text", + "id": "usertext", + "spacing": "none", + "isMultiLine": "true", + "placeholder": "add some text and submit" + } + ], + "actions": [ + { + "type": "Action.Submit", + "title": "Submit" + } + ] +} diff --git a/samples/54.teams-task-module/teamsAppManifest/manifest.json b/samples/54.teams-task-module/teamsAppManifest/manifest.json index 332a5661..9aaf9692 100644 --- a/samples/54.teams-task-module/teamsAppManifest/manifest.json +++ b/samples/54.teams-task-module/teamsAppManifest/manifest.json @@ -1,66 +1,42 @@ { - "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", - "manifestVersion": "1.5", - "version": "1.0.0", - "id": "<>", - "packageName": "com.teams.sample.teamsconversationbot", - "developer": { - "name": "teamsConversationBot", - "websiteUrl": "https://www.microsoft.com", - "privacyUrl": "https://www.teams.com/privacy", - "termsOfUseUrl": "https://www.teams.com/termsofuser" - }, - "icons": { - "outline": "icon-outline.png", - "color": "icon-color.png" - }, - "name": { - "short": "TeamsConversationBot", - "full": "TeamsConversationBot" - }, - "description": { - "short": "TeamsConversationBot", - "full": "TeamsConversationBot" - }, - "accentColor": "#FFFFFF", - "bots": [ - { - "botId": "<>", - "scopes": [ - "personal", - "groupchat", - "team" - ], - "supportsFiles": false, - "isNotificationOnly": false, - "commandLists": [ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", + "manifestVersion": "1.5", + "version": "1.0.0", + "id": "<>", + "packageName": "com.microsoft.teams.samples", + "developer": { + "name": "Microsoft", + "websiteUrl": "https://example.azurewebsites.net", + "privacyUrl": "https://example.azurewebsites.net/privacy", + "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" + }, + "icons": { + "color": "icon-color.png", + "outline": "icon-outline.png" + }, + "name": { + "short": "Task Module", + "full": "Simple Task Module" + }, + "description": { + "short": "Test Task Module Scenario", + "full": "Simple Task Module Scenario Test" + }, + "accentColor": "#FFFFFF", + "bots": [ { - "scopes": [ - "personal", - "groupchat", - "team" - ], - "commands": [ - { - "title": "MentionMe", - "description": "Sends message with @mention of the sender" - }, - { - "title": "Show Welcome", - "description": "Shows the welcome card" - }, - { - "title": "MessageAllMembers", - "description": "Send 1 to 1 message to all members of the current conversation" - } - ] + "botId": "<>", + "scopes": [ + "personal", + "team", + "groupchat" + ], + "supportsFiles": false, + "isNotificationOnly": false } - ] - } - ], - "permissions": [ - "identity", - "messageTeamMembers" - ], - "validDomains": [] + ], + "permissions": [ + "identity", + "messageTeamMembers" + ] } From 01f30e94d8534f510c9318b4c4cd3195dbad6b62 Mon Sep 17 00:00:00 2001 From: Emilio Munoz Date: Tue, 21 Apr 2020 03:37:49 -0700 Subject: [PATCH 3/4] Fixing readme and sample text messages --- .../teams/TeamsActivityHandlerTests.java | 2 +- samples/54.teams-task-module/README.md | 4 -- .../teamstaskmodule/TeamsTaskModuleBot.java | 40 +++++++++++++++---- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java index 9da0453e..a08b255b 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java @@ -878,7 +878,7 @@ public class TeamsActivityHandlerTests { } @Override - protected CompletableFuture onTeamsTaskModuleSubmit(TurnContext turnContext, + protected CompletableFuture onTeamsTaskModuleSubmit(TurnContext turnContext, TaskModuleRequest taskModuleRequest ) { record.add("onTeamsTaskModuleSubmit"); diff --git a/samples/54.teams-task-module/README.md b/samples/54.teams-task-module/README.md index 4b27ecac..723ecd72 100644 --- a/samples/54.teams-task-module/README.md +++ b/samples/54.teams-task-module/README.md @@ -53,10 +53,6 @@ the Teams service needs to call into the bot. You can interact with this bot by sending it a message. The bot will respond with a Hero Card with a button which will display a Task Module when clicked. The Task Module demonstrates retrieving input from a user through a Text Block and a Submit button. -### Avoiding Permission-Related Errors - -You may encounter permission-related errors when sending a proactive message. This can often be mitigated by using `MicrosoftAppCredentials.TrustServiceUrl()`. See [the documentation](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp#avoiding-401-unauthorized-errors) for more information. - ## 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. diff --git a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java index b53e37d8..5ac9eb37 100644 --- a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java @@ -3,6 +3,7 @@ package com.microsoft.bot.sample.teamstaskmodule; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.bot.builder.MessageFactory; @@ -14,6 +15,9 @@ import com.microsoft.bot.schema.Attachment; import com.microsoft.bot.schema.HeroCard; import com.microsoft.bot.schema.teams.*; import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.json.JSONObject; import org.springframework.stereotype.Component; @@ -37,6 +41,7 @@ import java.util.concurrent.CompletableFuture; public class TeamsTaskModuleBot extends TeamsActivityHandler { private String appId; private String appPassword; + private static final Logger botLogger = LogManager.getLogger(TeamsTaskModuleBot.class); public TeamsTaskModuleBot(Configuration configuration) { appId = configuration.getProperty("MicrosoftAppId"); @@ -66,7 +71,14 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { TurnContext turnContext, TaskModuleRequest taskModuleRequest) { - Activity reply = MessageFactory.text("onTeamsTaskModuleFetch TaskModuleRequest: " ); + Activity reply = null; + try { + reply = MessageFactory.text( + "onTeamsTaskModuleFetch TaskModuleRequest: " + + new ObjectMapper().writeValueAsString(taskModuleRequest)); + } catch (JsonProcessingException e) { + botLogger.log(Level.ERROR, e.getStackTrace()); + } turnContext.sendActivity(reply) .thenApply(resourceResponse -> null); @@ -91,7 +103,14 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { TurnContext turnContext, TaskModuleRequest taskModuleRequest){ - Activity reply = MessageFactory.text("onTeamsTaskModuleSubmit TaskModuleRequest: " ); + Activity reply = null; + try { + reply = MessageFactory.text( + "onTeamsTaskModuleSubmit TaskModuleRequest: " + + new ObjectMapper().writeValueAsString(taskModuleRequest)); + } catch (JsonProcessingException e) { + botLogger.log(Level.ERROR, e.getStackTrace()); + } turnContext.sendActivity(reply) .thenApply(resourceResponse -> null); @@ -108,10 +127,15 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { private Attachment getTaskModuleHeroCard() { HeroCard card = new HeroCard() {{ - setTitle(""); - setSubtitle("Click the buttons below to update this card"); + setTitle("Task Module Invocation from Hero Card"); + setSubtitle("This is a hero card with a Task Module Action button. Click the button to show an Adaptive Card within a Task Module."); setButtons(Arrays.asList( - new TaskModuleAction("Adaptive Card", new JSONObject().put("data", "adaptivecard").toString()) + new TaskModuleAction( + "Adaptive Card", + new JSONObject().put( + "data", + "adaptivecard" + ).toString()) )); }}; return card.toAttachment(); @@ -124,12 +148,12 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { return new Attachment(){{ setContentType("application/vnd.microsoft.card.adaptive"); - setContent(new ObjectMapper().readValue((String) result, ObjectNode.class)); + setContent(new ObjectMapper().readValue(result, ObjectNode.class)); }}; } catch (FileNotFoundException e) { - e.printStackTrace(); + botLogger.log(Level.ERROR, e.getStackTrace()); } catch (IOException e) { - e.printStackTrace(); + botLogger.log(Level.ERROR, e.getStackTrace()); } return new Attachment(); } From 760002c509ab524341a91b62f0474b7a1799bed3 Mon Sep 17 00:00:00 2001 From: tracyboehrer Date: Thu, 23 Apr 2020 09:17:01 -0500 Subject: [PATCH 4/4] Code style and exception handling changes. --- samples/54.teams-task-module/README.md | 9 +- .../sample/teamstaskmodule/Application.java | 7 +- .../teamstaskmodule/TeamsTaskModuleBot.java | 120 ++++++++---------- 3 files changed, 62 insertions(+), 74 deletions(-) diff --git a/samples/54.teams-task-module/README.md b/samples/54.teams-task-module/README.md index 723ecd72..1ee8f944 100644 --- a/samples/54.teams-task-module/README.md +++ b/samples/54.teams-task-module/README.md @@ -1,7 +1,4 @@ - -# Teams Conversation Bot - -Bot Framework v4 Conversation Bot sample for Teams. +# Teams Task Module Bot Framework Teams Task Module sample. @@ -44,10 +41,10 @@ the Teams service needs to call into the bot. 1) From the root of this project folder: - Build the sample using `mvn package` - Unless done previously, install the packages in the local cache by using `mvn install` - - Run it by using `java -jar .\target\bot-teams-conversation-sample.jar` + - Run it by using `java -jar .\target\bot-teams-task-module-sample.jar` -## Interacting with the bot +## Interacting with the bot in Teams > Note this `manifest.json` specified that the bot will be installed in "personal", "team" and "groupchat" scope which is why you immediately entered a one on one chat conversation with the bot. You can at mention the bot in a group chat or in a Channel in the Team you installed it in. Please refer to Teams documentation for more details. diff --git a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java index 67793bd0..3994a52c 100644 --- a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/Application.java @@ -14,9 +14,9 @@ import org.springframework.context.annotation.Import; /** * This is the starting point of the Sprint Boot Bot application. - * - * This class also provides overrides for dependency injections. A class that extends the - * {@link com.microsoft.bot.builder.Bot} interface should be annotated with @Component. + *

+ * This class also provides overrides for dependency injections. A class that extends the {@link + * com.microsoft.bot.builder.Bot} interface should be annotated with @Component. * * @see TeamsTaskModuleBot */ @@ -29,6 +29,7 @@ import org.springframework.context.annotation.Import; @Import({BotController.class}) public class Application extends BotDependencyConfiguration { + public static void main(String[] args) { SpringApplication.run(Application.class, args); } diff --git a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java index 5ac9eb37..05c62e77 100644 --- a/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java +++ b/samples/54.teams-task-module/src/main/java/com/microsoft/bot/sample/teamstaskmodule/TeamsTaskModuleBot.java @@ -9,20 +9,15 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.bot.builder.MessageFactory; import com.microsoft.bot.builder.TurnContext; import com.microsoft.bot.builder.teams.TeamsActivityHandler; -import com.microsoft.bot.integration.Configuration; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.Attachment; import com.microsoft.bot.schema.HeroCard; import com.microsoft.bot.schema.teams.*; +import java.util.concurrent.CompletionException; import org.apache.commons.io.IOUtils; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.json.JSONObject; import org.springframework.stereotype.Component; -import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -33,20 +28,12 @@ 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.

+ * 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.

*/ @Component public class TeamsTaskModuleBot extends TeamsActivityHandler { - private String appId; - private String appPassword; - private static final Logger botLogger = LogManager.getLogger(TeamsTaskModuleBot.class); - - public TeamsTaskModuleBot(Configuration configuration) { - appId = configuration.getProperty("MicrosoftAppId"); - appPassword = configuration.getProperty("MicrosoftAppPassword"); - } @Override protected CompletableFuture onTeamsMembersAdded( @@ -60,7 +47,8 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { @Override protected CompletableFuture onMessageActivity( - TurnContext turnContext) { + TurnContext turnContext + ) { Attachment attachment = getTaskModuleHeroCard(); return turnContext.sendActivity(MessageFactory.attachment(attachment)) .thenApply(resourceResponse -> null); @@ -69,92 +57,94 @@ public class TeamsTaskModuleBot extends TeamsActivityHandler { @Override protected CompletableFuture onTeamsTaskModuleFetch( TurnContext turnContext, - TaskModuleRequest taskModuleRequest) { - - Activity reply = null; + TaskModuleRequest taskModuleRequest + ) { + Activity reply; try { reply = MessageFactory.text( "onTeamsTaskModuleFetch TaskModuleRequest: " + - new ObjectMapper().writeValueAsString(taskModuleRequest)); + new ObjectMapper().writeValueAsString(taskModuleRequest)); } catch (JsonProcessingException e) { - botLogger.log(Level.ERROR, e.getStackTrace()); + CompletableFuture result = new CompletableFuture<>(); + result.completeExceptionally(new CompletionException(e)); + return result; } - turnContext.sendActivity(reply) - .thenApply(resourceResponse -> null); + return turnContext.sendActivity(reply) + .thenApply(resourceResponse -> { + Attachment adaptiveCard = getTaskModuleAdaptiveCard(); - Attachment adaptiveCard = getTaskModuleAdaptiveCard(); - - return CompletableFuture.completedFuture(new TaskModuleResponse(){{ - setTask(new TaskModuleContinueResponse(){{ - setType("continue"); - setValue(new TaskModuleTaskInfo(){{ - setCard(adaptiveCard); - setHeight(200); - setWidth(400); - setTitle("Adaptive Card: Inputs"); - }}); - }}); - }}); + return new TaskModuleResponse() {{ + setTask(new TaskModuleContinueResponse() {{ + setType("continue"); + setValue(new TaskModuleTaskInfo() {{ + setCard(adaptiveCard); + setHeight(200); + setWidth(400); + setTitle("Adaptive Card: Inputs"); + }}); + }}); + }}; + }); } @Override protected CompletableFuture onTeamsTaskModuleSubmit( TurnContext turnContext, - TaskModuleRequest taskModuleRequest){ - - Activity reply = null; + TaskModuleRequest taskModuleRequest + ) { + Activity reply; try { reply = MessageFactory.text( "onTeamsTaskModuleSubmit TaskModuleRequest: " + new ObjectMapper().writeValueAsString(taskModuleRequest)); } catch (JsonProcessingException e) { - botLogger.log(Level.ERROR, e.getStackTrace()); + CompletableFuture result = new CompletableFuture<>(); + result.completeExceptionally(new CompletionException(e)); + return result; } turnContext.sendActivity(reply) .thenApply(resourceResponse -> null); - return CompletableFuture.completedFuture(new TaskModuleResponse(){{ - setTask(new TaskModuleMessageResponse(){{ + return CompletableFuture.completedFuture(new TaskModuleResponse() {{ + setTask(new TaskModuleMessageResponse() {{ setType("message"); setValue("Thanks!"); }}); }}); - } - private Attachment getTaskModuleHeroCard() - { - HeroCard card = new HeroCard() {{ - setTitle("Task Module Invocation from Hero Card"); - setSubtitle("This is a hero card with a Task Module Action button. Click the button to show an Adaptive Card within a Task Module."); - setButtons(Arrays.asList( - new TaskModuleAction( - "Adaptive Card", - new JSONObject().put( - "data", - "adaptivecard" - ).toString()) + private Attachment getTaskModuleHeroCard() { + HeroCard card = new HeroCard() {{ + setTitle("Task Module Invocation from Hero Card"); + setSubtitle( + "This is a hero card with a Task Module Action button. Click the button to show an Adaptive Card within a Task Module."); + setButtons(Arrays.asList( + new TaskModuleAction( + "Adaptive Card", + new JSONObject().put( + "data", + "adaptivecard" + ).toString() + ) )); }}; return card.toAttachment(); } - private Attachment getTaskModuleAdaptiveCard(){ + private Attachment getTaskModuleAdaptiveCard() { try { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("adaptivecard.json"); + InputStream inputStream = getClass().getClassLoader() + .getResourceAsStream("adaptivecard.json"); String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - return new Attachment(){{ + return new Attachment() {{ setContentType("application/vnd.microsoft.card.adaptive"); setContent(new ObjectMapper().readValue(result, ObjectNode.class)); }}; - } catch (FileNotFoundException e) { - botLogger.log(Level.ERROR, e.getStackTrace()); - } catch (IOException e) { - botLogger.log(Level.ERROR, e.getStackTrace()); + } catch (Throwable t) { + throw new CompletionException(t); } - return new Attachment(); } }