diff --git a/pom.xml b/pom.xml index 2a9a7eec6..488cdd1a5 100644 --- a/pom.xml +++ b/pom.xml @@ -375,6 +375,10 @@ samples/16.proactive-messages samples/45.state-management samples/47.inspection + samples/50.teams-messaging-extensions-search + samples/51.teams-messaging-extensions-action + samples/52.teams-messaging-extensions-search-auth-config + samples/53.teams-messaging-extensions-action-preview samples/57.teams-conversation-bot diff --git a/samples/50.teams-messaging-extensions-search/LICENSE b/samples/50.teams-messaging-extensions-search/LICENSE new file mode 100644 index 000000000..21071075c --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/README.md b/samples/50.teams-messaging-extensions-search/README.md new file mode 100644 index 000000000..6dd585698 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/README.md @@ -0,0 +1,76 @@ + +# Teams Messaging Extensions Search 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-message-extension-search.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/50.teams-messaging-extensions-search/deploymentTemplates/new-rg-parameters.json b/samples/50.teams-messaging-extensions-search/deploymentTemplates/new-rg-parameters.json new file mode 100644 index 000000000..ead339093 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/deploymentTemplates/preexisting-rg-parameters.json b/samples/50.teams-messaging-extensions-search/deploymentTemplates/preexisting-rg-parameters.json new file mode 100644 index 000000000..b6f5114fc --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/deploymentTemplates/template-with-new-rg.json b/samples/50.teams-messaging-extensions-search/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..dcd6260a5 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/deploymentTemplates/template-with-preexisting-rg.json b/samples/50.teams-messaging-extensions-search/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..b790d2bdc --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/pom.xml b/samples/50.teams-messaging-extensions-search/pom.xml new file mode 100644 index 000000000..5f3e94027 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/pom.xml @@ -0,0 +1,315 @@ + + + + 4.0.0 + + com.microsoft.bot.sample + bot-teams-message-extensions-search + sample + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Teams Messaging Extensions Search 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.teamssearch.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.12 + test + + + 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.teamssearch.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-teamssearch-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/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/Application.java b/samples/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/Application.java new file mode 100644 index 000000000..c52248fad --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/Application.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearch; + +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 TeamsConversationBot + */ +@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/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/TeamsMessagingExtensionsSearchBot.java b/samples/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/TeamsMessagingExtensionsSearchBot.java new file mode 100644 index 000000000..c45f4922c --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/src/main/java/com/microsoft/bot/sample/teamssearch/TeamsMessagingExtensionsSearchBot.java @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearch; + +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.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.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +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.

+ */ +@Component +public class TeamsMessagingExtensionsSearchBot extends TeamsActivityHandler { + private String appId; + private String appPassword; + + public TeamsMessagingExtensionsSearchBot(Configuration configuration) { + appId = configuration.getProperty("MicrosoftAppId"); + appPassword = configuration.getProperty("MicrosoftAppPassword"); + } + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + turnContext.getActivity().removeRecipientMention(); + + switch (turnContext.getActivity().getText().trim()) { + case "MentionMe": + return mentionActivity(turnContext); + + case "UpdateCardAction": + return updateCardActivity(turnContext); + + case "Delete": + return deleteCardActivity(turnContext); + + case "MessageAllMembers": + return messageAllMembers(turnContext); + + default: + // This will come back deserialized as a Map. + Object value = new Object() { + int count = 0; + }; + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Click the buttons below to update this card"); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(value); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }} + )); + }}; + + return turnContext.sendActivity(MessageFactory.attachment(card.toAttachment())) + .thenApply(resourceResponse -> null); + } + } + + @Override + protected CompletableFuture onTeamsMembersAdded( + List membersAdded, + TeamInfo teamInfo, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter(member -> !StringUtils.equals(member.getId(), turnContext.getActivity().getRecipient().getId())) + .map(channel -> turnContext.sendActivity( + MessageFactory.text("Welcome to the team " + channel.getGivenName() + " " + channel.getSurname() + "."))) + .collect(CompletableFutures.toFutureList()) + .thenApply(resourceResponses -> null); + } + + private CompletableFuture deleteCardActivity(TurnContext turnContext) { + return turnContext.deleteActivity(turnContext.getActivity().getReplyToId()); + } + + // If you encounter permission-related errors when sending this message, see + // https://aka.ms/BotTrustServiceUrl + private CompletableFuture messageAllMembers(TurnContext turnContext) { + String teamsChannelId = turnContext.getActivity().teamsGetChannelId(); + String serviceUrl = turnContext.getActivity().getServiceUrl(); + MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(appId, appPassword); + + return TeamsInfo.getMembers(turnContext) + .thenCompose(members -> { + List> conversations = new ArrayList<>(); + + // Send a message to each member. These will all go out + // at the same time. + for (TeamsChannelAccount member : members) { + Activity proactiveMessage = MessageFactory.text( + "Hello " + member.getGivenName() + " " + member.getSurname() + + ". I'm a Teams conversation bot."); + + ConversationParameters conversationParameters = new ConversationParameters() {{ + setIsGroup(false); + setBot(turnContext.getActivity().getRecipient()); + setMembers(Collections.singletonList(member)); + setTenantId(turnContext.getActivity().getConversation().getTenantId()); + }}; + + conversations.add( + ((BotFrameworkAdapter) turnContext.getAdapter()).createConversation( + teamsChannelId, + serviceUrl, + credentials, + conversationParameters, + (context) -> { + ConversationReference reference = context.getActivity().getConversationReference(); + return context.getAdapter().continueConversation( + appId, + reference, + (inner_context) -> inner_context.sendActivity(proactiveMessage) + .thenApply(resourceResponse -> null) + ); + } + ) + ); + } + + return CompletableFuture.allOf(conversations.toArray(new CompletableFuture[0])); + }) + // After all member messages are sent, send confirmation to the user. + .thenApply(conversations -> turnContext.sendActivity(MessageFactory.text("All messages have been sent."))) + .thenApply(allSent -> null); + } + + private CompletableFuture updateCardActivity(TurnContext turnContext) { + Map data = (Map) turnContext.getActivity().getValue(); + data.put("count", (int) data.get("count") + 1); + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Update count - " + data.get("count")); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(data); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Delete card"); + setText("Delete"); + }} + )); + }}; + + Activity updatedActivity = MessageFactory.attachment(card.toAttachment()); + updatedActivity.setId(turnContext.getActivity().getReplyToId()); + + return turnContext.updateActivity(updatedActivity) + .thenApply(resourceResponse -> null); + } + + private CompletableFuture mentionActivity(TurnContext turnContext) { + Mention mention = new Mention(); + mention.setMentioned(turnContext.getActivity().getFrom()); + mention.setText("" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + ""); + + Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'"); + replyActivity.setMentions(Collections.singletonList(mention)); + + return turnContext.sendActivity(replyActivity) + .thenApply(resourceResponse -> null); + } +} diff --git a/samples/50.teams-messaging-extensions-search/src/main/resources/application.properties b/samples/50.teams-messaging-extensions-search/src/main/resources/application.properties new file mode 100644 index 000000000..a695b3bf0 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/src/main/resources/application.properties @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= diff --git a/samples/50.teams-messaging-extensions-search/src/main/resources/log4j2.json b/samples/50.teams-messaging-extensions-search/src/main/resources/log4j2.json new file mode 100644 index 000000000..67c0ad530 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/src/main/webapp/META-INF/MANIFEST.MF b/samples/50.teams-messaging-extensions-search/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/50.teams-messaging-extensions-search/src/main/webapp/WEB-INF/web.xml b/samples/50.teams-messaging-extensions-search/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/src/main/webapp/index.html b/samples/50.teams-messaging-extensions-search/src/main/webapp/index.html new file mode 100644 index 000000000..40b74c007 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/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/50.teams-messaging-extensions-search/src/test/java/com/microsoft/bot/sample/teamssearch/ApplicationTests.java b/samples/50.teams-messaging-extensions-search/src/test/java/com/microsoft/bot/sample/teamssearch/ApplicationTests.java new file mode 100644 index 000000000..43ab7a1b2 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/src/test/java/com/microsoft/bot/sample/teamssearch/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearch; + +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/50.teams-messaging-extensions-search/teamsAppManifest/icon-color.png b/samples/50.teams-messaging-extensions-search/teamsAppManifest/icon-color.png new file mode 100644 index 000000000..48a2de133 Binary files /dev/null and b/samples/50.teams-messaging-extensions-search/teamsAppManifest/icon-color.png differ diff --git a/samples/50.teams-messaging-extensions-search/teamsAppManifest/icon-outline.png b/samples/50.teams-messaging-extensions-search/teamsAppManifest/icon-outline.png new file mode 100644 index 000000000..dbfa92772 Binary files /dev/null and b/samples/50.teams-messaging-extensions-search/teamsAppManifest/icon-outline.png differ diff --git a/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json b/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json new file mode 100644 index 000000000..cd4900030 --- /dev/null +++ b/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", + "manifestVersion": "1.5", + "version": "1.0", + "id": "<>", + "packageName": "com.microsoft.teams.samples.searchExtension", + "developer": { + "name": "Microsoft Corp", + "websiteUrl": "https://example.azurewebsites.net", + "privacyUrl": "https://example.azurewebsites.net/privacy", + "termsOfUseUrl": "https://example.azurewebsites.net/termsofuse" + }, + "name": { + "short": "search-extension-settings", + "full": "Microsoft Teams V4 Search Messaging Extension Bot and settings" + }, + "description": { + "short": "Microsoft Teams V4 Search Messaging Extension Bot and settings", + "full": "Sample Search Messaging Extension Bot using V4 Bot Builder SDK and V4 Microsoft Teams Extension SDK" + }, + "icons": { + "outline": "icon-outline.png", + "color": "icon-color.png" + }, + "accentColor": "#abcdef", + "composeExtensions": [ + { + "botId": "<>", + "canUpdateConfiguration": true, + "commands": [ + { + "id": "searchQuery", + "context": [ "compose", "commandBox" ], + "description": "Test command to run query", + "title": "Search", + "type": "query", + "parameters": [ + { + "name": "searchQuery", + "title": "Search Query", + "description": "Your search query", + "inputType": "text" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/samples/51.teams-messaging-extensions-action/LICENSE b/samples/51.teams-messaging-extensions-action/LICENSE new file mode 100644 index 000000000..21071075c --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/README.md b/samples/51.teams-messaging-extensions-action/README.md new file mode 100644 index 000000000..eed334ee8 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/README.md @@ -0,0 +1,76 @@ + +# Teams Message Extension Action 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-messaging-extensions-action-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/51.teams-messaging-extensions-action/deploymentTemplates/new-rg-parameters.json b/samples/51.teams-messaging-extensions-action/deploymentTemplates/new-rg-parameters.json new file mode 100644 index 000000000..ead339093 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/deploymentTemplates/preexisting-rg-parameters.json b/samples/51.teams-messaging-extensions-action/deploymentTemplates/preexisting-rg-parameters.json new file mode 100644 index 000000000..b6f5114fc --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/deploymentTemplates/template-with-new-rg.json b/samples/51.teams-messaging-extensions-action/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..dcd6260a5 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/deploymentTemplates/template-with-preexisting-rg.json b/samples/51.teams-messaging-extensions-action/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..b790d2bdc --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/pom.xml b/samples/51.teams-messaging-extensions-action/pom.xml new file mode 100644 index 000000000..4b9e97619 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/pom.xml @@ -0,0 +1,315 @@ + + + + 4.0.0 + + com.microsoft.bot.sample + bot-teams-messaging-extensions-action + sample + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Teams Messaging Extensions Action 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.teamsaction.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.12 + test + + + 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.teamsaction.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-teamsaction-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/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/Application.java b/samples/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/Application.java new file mode 100644 index 000000000..d7e433180 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/Application.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsaction; + +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 TeamsConversationBot + */ +@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/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/TeamsMessagingExtensionsActionBot.java b/samples/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/TeamsMessagingExtensionsActionBot.java new file mode 100644 index 000000000..ed13ac0eb --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/src/main/java/com/microsoft/bot/sample/teamsaction/TeamsMessagingExtensionsActionBot.java @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsaction; + +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.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.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +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.

+ */ +@Component +public class TeamsMessagingExtensionsActionBot extends TeamsActivityHandler { + private String appId; + private String appPassword; + + public TeamsMessagingExtensionsActionBot(Configuration configuration) { + appId = configuration.getProperty("MicrosoftAppId"); + appPassword = configuration.getProperty("MicrosoftAppPassword"); + } + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + turnContext.getActivity().removeRecipientMention(); + + switch (turnContext.getActivity().getText().trim()) { + case "MentionMe": + return mentionActivity(turnContext); + + case "UpdateCardAction": + return updateCardActivity(turnContext); + + case "Delete": + return deleteCardActivity(turnContext); + + case "MessageAllMembers": + return messageAllMembers(turnContext); + + default: + // This will come back deserialized as a Map. + Object value = new Object() { + int count = 0; + }; + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Click the buttons below to update this card"); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(value); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }} + )); + }}; + + return turnContext.sendActivity(MessageFactory.attachment(card.toAttachment())) + .thenApply(resourceResponse -> null); + } + } + + @Override + protected CompletableFuture onTeamsMembersAdded( + List membersAdded, + TeamInfo teamInfo, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter(member -> !StringUtils.equals(member.getId(), turnContext.getActivity().getRecipient().getId())) + .map(channel -> turnContext.sendActivity( + MessageFactory.text("Welcome to the team " + channel.getGivenName() + " " + channel.getSurname() + "."))) + .collect(CompletableFutures.toFutureList()) + .thenApply(resourceResponses -> null); + } + + private CompletableFuture deleteCardActivity(TurnContext turnContext) { + return turnContext.deleteActivity(turnContext.getActivity().getReplyToId()); + } + + // If you encounter permission-related errors when sending this message, see + // https://aka.ms/BotTrustServiceUrl + private CompletableFuture messageAllMembers(TurnContext turnContext) { + String teamsChannelId = turnContext.getActivity().teamsGetChannelId(); + String serviceUrl = turnContext.getActivity().getServiceUrl(); + MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(appId, appPassword); + + return TeamsInfo.getMembers(turnContext) + .thenCompose(members -> { + List> conversations = new ArrayList<>(); + + // Send a message to each member. These will all go out + // at the same time. + for (TeamsChannelAccount member : members) { + Activity proactiveMessage = MessageFactory.text( + "Hello " + member.getGivenName() + " " + member.getSurname() + + ". I'm a Teams conversation bot."); + + ConversationParameters conversationParameters = new ConversationParameters() {{ + setIsGroup(false); + setBot(turnContext.getActivity().getRecipient()); + setMembers(Collections.singletonList(member)); + setTenantId(turnContext.getActivity().getConversation().getTenantId()); + }}; + + conversations.add( + ((BotFrameworkAdapter) turnContext.getAdapter()).createConversation( + teamsChannelId, + serviceUrl, + credentials, + conversationParameters, + (context) -> { + ConversationReference reference = context.getActivity().getConversationReference(); + return context.getAdapter().continueConversation( + appId, + reference, + (inner_context) -> inner_context.sendActivity(proactiveMessage) + .thenApply(resourceResponse -> null) + ); + } + ) + ); + } + + return CompletableFuture.allOf(conversations.toArray(new CompletableFuture[0])); + }) + // After all member messages are sent, send confirmation to the user. + .thenApply(conversations -> turnContext.sendActivity(MessageFactory.text("All messages have been sent."))) + .thenApply(allSent -> null); + } + + private CompletableFuture updateCardActivity(TurnContext turnContext) { + Map data = (Map) turnContext.getActivity().getValue(); + data.put("count", (int) data.get("count") + 1); + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Update count - " + data.get("count")); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(data); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Delete card"); + setText("Delete"); + }} + )); + }}; + + Activity updatedActivity = MessageFactory.attachment(card.toAttachment()); + updatedActivity.setId(turnContext.getActivity().getReplyToId()); + + return turnContext.updateActivity(updatedActivity) + .thenApply(resourceResponse -> null); + } + + private CompletableFuture mentionActivity(TurnContext turnContext) { + Mention mention = new Mention(); + mention.setMentioned(turnContext.getActivity().getFrom()); + mention.setText("" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + ""); + + Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'"); + replyActivity.setMentions(Collections.singletonList(mention)); + + return turnContext.sendActivity(replyActivity) + .thenApply(resourceResponse -> null); + } +} diff --git a/samples/51.teams-messaging-extensions-action/src/main/resources/application.properties b/samples/51.teams-messaging-extensions-action/src/main/resources/application.properties new file mode 100644 index 000000000..a695b3bf0 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/src/main/resources/application.properties @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= diff --git a/samples/51.teams-messaging-extensions-action/src/main/resources/log4j2.json b/samples/51.teams-messaging-extensions-action/src/main/resources/log4j2.json new file mode 100644 index 000000000..67c0ad530 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/src/main/webapp/META-INF/MANIFEST.MF b/samples/51.teams-messaging-extensions-action/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/51.teams-messaging-extensions-action/src/main/webapp/WEB-INF/web.xml b/samples/51.teams-messaging-extensions-action/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/src/main/webapp/index.html b/samples/51.teams-messaging-extensions-action/src/main/webapp/index.html new file mode 100644 index 000000000..40b74c007 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/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/51.teams-messaging-extensions-action/src/test/java/com/microsoft/bot/sample/teamsaction/ApplicationTests.java b/samples/51.teams-messaging-extensions-action/src/test/java/com/microsoft/bot/sample/teamsaction/ApplicationTests.java new file mode 100644 index 000000000..aa0503096 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/src/test/java/com/microsoft/bot/sample/teamsaction/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsaction; + +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/51.teams-messaging-extensions-action/teamsAppManifest/icon-color.png b/samples/51.teams-messaging-extensions-action/teamsAppManifest/icon-color.png new file mode 100644 index 000000000..48a2de133 Binary files /dev/null and b/samples/51.teams-messaging-extensions-action/teamsAppManifest/icon-color.png differ diff --git a/samples/51.teams-messaging-extensions-action/teamsAppManifest/icon-outline.png b/samples/51.teams-messaging-extensions-action/teamsAppManifest/icon-outline.png new file mode 100644 index 000000000..dbfa92772 Binary files /dev/null and b/samples/51.teams-messaging-extensions-action/teamsAppManifest/icon-outline.png differ diff --git a/samples/51.teams-messaging-extensions-action/teamsAppManifest/manifest.json b/samples/51.teams-messaging-extensions-action/teamsAppManifest/manifest.json new file mode 100644 index 000000000..0dcba1562 --- /dev/null +++ b/samples/51.teams-messaging-extensions-action/teamsAppManifest/manifest.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", + "manifestVersion": "1.5", + "version": "1.0", + "id": "<>", + "packageName": "com.microsoft.teams.samples", + "developer": { + "name": "Microsoft", + "websiteUrl": "https://dev.botframework.com", + "privacyUrl": "https://privacy.microsoft.com", + "termsOfUseUrl": "https://www.microsoft.com/en-us/legal/intellectualproperty/copyright/default.aspx" + }, + "name": { + "short": "Action Messaging Extension", + "full": "Microsoft Teams Action Based Messaging Extension" + }, + "description": { + "short": "Sample demonstrating an Action Based Messaging Extension", + "full": "Sample Action Messaging Extension built with the Bot Builder SDK" + }, + "icons": { + "outline": "icon-outline.png", + "color": "icon-color.png" + }, + "accentColor": "#FFFFFF", + "composeExtensions": [ + { + "botId": "<>", + "commands": [ + { + "id": "createCard", + "type": "action", + "context": [ "compose" ], + "description": "Command to run action to create a Card from Compose Box", + "title": "Create Card", + "parameters": [ + { + "name": "title", + "title": "Card title", + "description": "Title for the card", + "inputType": "text" + }, + { + "name": "subTitle", + "title": "Subtitle", + "description": "Subtitle for the card", + "inputType": "text" + }, + { + "name": "text", + "title": "Text", + "description": "Text for the card", + "inputType": "textarea" + } + ] + }, + { + "id": "shareMessage", + "type": "action", + "context": [ "message" ], + "description": "Test command to run action on message context (message sharing)", + "title": "Share Message", + "parameters": [ + { + "name": "includeImage", + "title": "Include Image", + "description": "Include image in Hero Card", + "inputType": "toggle" + } + ] + } + ] + } + ], + "permissions": [ + "identity" + ] +} diff --git a/samples/52.teams-messaging-extensions-search-auth-config/LICENSE b/samples/52.teams-messaging-extensions-search-auth-config/LICENSE new file mode 100644 index 000000000..21071075c --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/README.md b/samples/52.teams-messaging-extensions-search-auth-config/README.md new file mode 100644 index 000000000..7dad1bf68 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/README.md @@ -0,0 +1,76 @@ + +# Teams Messaging Extension Search Auth Config 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-messaging-extensions-search-auth-config-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/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/new-rg-parameters.json b/samples/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/new-rg-parameters.json new file mode 100644 index 000000000..ead339093 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/preexisting-rg-parameters.json b/samples/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/preexisting-rg-parameters.json new file mode 100644 index 000000000..b6f5114fc --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/template-with-new-rg.json b/samples/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..dcd6260a5 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/template-with-preexisting-rg.json b/samples/52.teams-messaging-extensions-search-auth-config/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..b790d2bdc --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/pom.xml b/samples/52.teams-messaging-extensions-search-auth-config/pom.xml new file mode 100644 index 000000000..4f9f9771a --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/pom.xml @@ -0,0 +1,315 @@ + + + + 4.0.0 + + com.microsoft.bot.sample + bot-teams-messaging-extensions-search-auth-config + sample + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Teams Messaging Extensions Search Auth Config 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.teamssearchauth.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.12 + test + + + 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.teamssearchauth.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-teamssearchauth-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/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/Application.java b/samples/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/Application.java new file mode 100644 index 000000000..d3bd10099 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/Application.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearchauth; + +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 TeamsConversationBot + */ +@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/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/TeamsMessagingExtensionsSearchAuthConfigBot.java b/samples/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/TeamsMessagingExtensionsSearchAuthConfigBot.java new file mode 100644 index 000000000..fdc7cc3df --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/src/main/java/com/microsoft/bot/sample/teamssearchauth/TeamsMessagingExtensionsSearchAuthConfigBot.java @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearchauth; + +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.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.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +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.

+ */ +@Component +public class TeamsMessagingExtensionsSearchAuthConfigBot extends TeamsActivityHandler { + private String appId; + private String appPassword; + + public TeamsMessagingExtensionsSearchAuthConfigBot(Configuration configuration) { + appId = configuration.getProperty("MicrosoftAppId"); + appPassword = configuration.getProperty("MicrosoftAppPassword"); + } + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + turnContext.getActivity().removeRecipientMention(); + + switch (turnContext.getActivity().getText().trim()) { + case "MentionMe": + return mentionActivity(turnContext); + + case "UpdateCardAction": + return updateCardActivity(turnContext); + + case "Delete": + return deleteCardActivity(turnContext); + + case "MessageAllMembers": + return messageAllMembers(turnContext); + + default: + // This will come back deserialized as a Map. + Object value = new Object() { + int count = 0; + }; + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Click the buttons below to update this card"); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(value); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }} + )); + }}; + + return turnContext.sendActivity(MessageFactory.attachment(card.toAttachment())) + .thenApply(resourceResponse -> null); + } + } + + @Override + protected CompletableFuture onTeamsMembersAdded( + List membersAdded, + TeamInfo teamInfo, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter(member -> !StringUtils.equals(member.getId(), turnContext.getActivity().getRecipient().getId())) + .map(channel -> turnContext.sendActivity( + MessageFactory.text("Welcome to the team " + channel.getGivenName() + " " + channel.getSurname() + "."))) + .collect(CompletableFutures.toFutureList()) + .thenApply(resourceResponses -> null); + } + + private CompletableFuture deleteCardActivity(TurnContext turnContext) { + return turnContext.deleteActivity(turnContext.getActivity().getReplyToId()); + } + + // If you encounter permission-related errors when sending this message, see + // https://aka.ms/BotTrustServiceUrl + private CompletableFuture messageAllMembers(TurnContext turnContext) { + String teamsChannelId = turnContext.getActivity().teamsGetChannelId(); + String serviceUrl = turnContext.getActivity().getServiceUrl(); + MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(appId, appPassword); + + return TeamsInfo.getMembers(turnContext) + .thenCompose(members -> { + List> conversations = new ArrayList<>(); + + // Send a message to each member. These will all go out + // at the same time. + for (TeamsChannelAccount member : members) { + Activity proactiveMessage = MessageFactory.text( + "Hello " + member.getGivenName() + " " + member.getSurname() + + ". I'm a Teams conversation bot."); + + ConversationParameters conversationParameters = new ConversationParameters() {{ + setIsGroup(false); + setBot(turnContext.getActivity().getRecipient()); + setMembers(Collections.singletonList(member)); + setTenantId(turnContext.getActivity().getConversation().getTenantId()); + }}; + + conversations.add( + ((BotFrameworkAdapter) turnContext.getAdapter()).createConversation( + teamsChannelId, + serviceUrl, + credentials, + conversationParameters, + (context) -> { + ConversationReference reference = context.getActivity().getConversationReference(); + return context.getAdapter().continueConversation( + appId, + reference, + (inner_context) -> inner_context.sendActivity(proactiveMessage) + .thenApply(resourceResponse -> null) + ); + } + ) + ); + } + + return CompletableFuture.allOf(conversations.toArray(new CompletableFuture[0])); + }) + // After all member messages are sent, send confirmation to the user. + .thenApply(conversations -> turnContext.sendActivity(MessageFactory.text("All messages have been sent."))) + .thenApply(allSent -> null); + } + + private CompletableFuture updateCardActivity(TurnContext turnContext) { + Map data = (Map) turnContext.getActivity().getValue(); + data.put("count", (int) data.get("count") + 1); + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Update count - " + data.get("count")); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(data); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Delete card"); + setText("Delete"); + }} + )); + }}; + + Activity updatedActivity = MessageFactory.attachment(card.toAttachment()); + updatedActivity.setId(turnContext.getActivity().getReplyToId()); + + return turnContext.updateActivity(updatedActivity) + .thenApply(resourceResponse -> null); + } + + private CompletableFuture mentionActivity(TurnContext turnContext) { + Mention mention = new Mention(); + mention.setMentioned(turnContext.getActivity().getFrom()); + mention.setText("" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + ""); + + Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'"); + replyActivity.setMentions(Collections.singletonList(mention)); + + return turnContext.sendActivity(replyActivity) + .thenApply(resourceResponse -> null); + } +} diff --git a/samples/52.teams-messaging-extensions-search-auth-config/src/main/resources/application.properties b/samples/52.teams-messaging-extensions-search-auth-config/src/main/resources/application.properties new file mode 100644 index 000000000..a695b3bf0 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/src/main/resources/application.properties @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= diff --git a/samples/52.teams-messaging-extensions-search-auth-config/src/main/resources/log4j2.json b/samples/52.teams-messaging-extensions-search-auth-config/src/main/resources/log4j2.json new file mode 100644 index 000000000..67c0ad530 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/src/main/webapp/META-INF/MANIFEST.MF b/samples/52.teams-messaging-extensions-search-auth-config/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/52.teams-messaging-extensions-search-auth-config/src/main/webapp/WEB-INF/web.xml b/samples/52.teams-messaging-extensions-search-auth-config/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/src/main/webapp/index.html b/samples/52.teams-messaging-extensions-search-auth-config/src/main/webapp/index.html new file mode 100644 index 000000000..40b74c007 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/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/52.teams-messaging-extensions-search-auth-config/src/test/java/com/microsoft/bot/sample/teamssearchauth/ApplicationTests.java b/samples/52.teams-messaging-extensions-search-auth-config/src/test/java/com/microsoft/bot/sample/teamssearchauth/ApplicationTests.java new file mode 100644 index 000000000..01c209f17 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/src/test/java/com/microsoft/bot/sample/teamssearchauth/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamssearchauth; + +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/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-color.png b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-color.png new file mode 100644 index 000000000..48a2de133 Binary files /dev/null and b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-color.png differ diff --git a/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-outline.png b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-outline.png new file mode 100644 index 000000000..dbfa92772 Binary files /dev/null and b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/icon-outline.png differ diff --git a/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/manifest.json b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/manifest.json new file mode 100644 index 000000000..610027187 --- /dev/null +++ b/samples/52.teams-messaging-extensions-search-auth-config/teamsAppManifest/manifest.json @@ -0,0 +1,82 @@ +{ + "$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.sample", + "developer": { + "name": "Microsoft", + "websiteUrl": "https://dev.botframework.com", + "privacyUrl": "https://dev.botframework.com", + "termsOfUseUrl": "https://dev.botframework.com" + }, + "icons": { + "color": "icon-color.png", + "outline": "icon-outline.png" + }, + "name": { + "short": "Config Auth Search", + "full": "Config Auth Search" + }, + "description": { + "short": "Config Auth Search", + "full": "Config Auth Search" + }, + "accentColor": "#FFFFFF", + "composeExtensions": [ + { + "botId": "<>", + "canUpdateConfiguration": true, + "commands": [ + { + "id": "searchQuery", + "type": "query", + "title": "Search", + "description": "Perform a search", + "initialRun": false, + "fetchTask": false, + "context": [ + "commandBox", + "compose", + "message" + ], + "parameters": [ + { + "name": "searchQuery", + "title": "Search", + "description": "Your search query", + "inputType": "text" + } + ] + }, + { + "id": "SignOutCommand", + "type": "action", + "title": "Sign Out", + "description": "Sign out from authenticated services.", + "initialRun": false, + "fetchTask": true, + "context": [ + "commandBox", + "compose" + ], + "parameters": [ + { + "name": "param", + "title": "param", + "description": "" + } + ] + } + ] + } + ], + "permissions": [ + "identity" + ], + "validDomains": [ + "*.githubusercontent.com", + "*.ngrok.io", + "*.botframework.com" + ] +} diff --git a/samples/53.teams-messaging-extensions-action-preview/LICENSE b/samples/53.teams-messaging-extensions-action-preview/LICENSE new file mode 100644 index 000000000..21071075c --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/README.md b/samples/53.teams-messaging-extensions-action-preview/README.md new file mode 100644 index 000000000..f62acf508 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/README.md @@ -0,0 +1,76 @@ + +# Teams Messaging Extensions Action Preview 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-messaging-extensions-action-preview-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/53.teams-messaging-extensions-action-preview/deploymentTemplates/new-rg-parameters.json b/samples/53.teams-messaging-extensions-action-preview/deploymentTemplates/new-rg-parameters.json new file mode 100644 index 000000000..ead339093 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/deploymentTemplates/preexisting-rg-parameters.json b/samples/53.teams-messaging-extensions-action-preview/deploymentTemplates/preexisting-rg-parameters.json new file mode 100644 index 000000000..b6f5114fc --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/deploymentTemplates/template-with-new-rg.json b/samples/53.teams-messaging-extensions-action-preview/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..dcd6260a5 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/deploymentTemplates/template-with-preexisting-rg.json b/samples/53.teams-messaging-extensions-action-preview/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..b790d2bdc --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/pom.xml b/samples/53.teams-messaging-extensions-action-preview/pom.xml new file mode 100644 index 000000000..9c0bd133a --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/pom.xml @@ -0,0 +1,315 @@ + + + + 4.0.0 + + com.microsoft.bot.sample + bot-teams-messaging-extensions-action-preview + sample + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Teams Messaging Extensions Action Preview 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.teamsactionpreview.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.12 + test + + + 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.teamsactionpreview.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-teamsactionpreview-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/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/Application.java b/samples/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/Application.java new file mode 100644 index 000000000..5ebadb771 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/Application.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsactionpreview; + +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 TeamsConversationBot + */ +@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/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/TeamsMessagingExtensionsActionPreviewBot.java b/samples/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/TeamsMessagingExtensionsActionPreviewBot.java new file mode 100644 index 000000000..fe9d8c818 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/src/main/java/com/microsoft/bot/sample/teamsactionpreview/TeamsMessagingExtensionsActionPreviewBot.java @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsactionpreview; + +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.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.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +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.

+ */ +@Component +public class TeamsMessagingExtensionsActionPreviewBot extends TeamsActivityHandler { + private String appId; + private String appPassword; + + public TeamsMessagingExtensionsActionPreviewBot(Configuration configuration) { + appId = configuration.getProperty("MicrosoftAppId"); + appPassword = configuration.getProperty("MicrosoftAppPassword"); + } + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + turnContext.getActivity().removeRecipientMention(); + + switch (turnContext.getActivity().getText().trim()) { + case "MentionMe": + return mentionActivity(turnContext); + + case "UpdateCardAction": + return updateCardActivity(turnContext); + + case "Delete": + return deleteCardActivity(turnContext); + + case "MessageAllMembers": + return messageAllMembers(turnContext); + + default: + // This will come back deserialized as a Map. + Object value = new Object() { + int count = 0; + }; + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Click the buttons below to update this card"); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(value); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }} + )); + }}; + + return turnContext.sendActivity(MessageFactory.attachment(card.toAttachment())) + .thenApply(resourceResponse -> null); + } + } + + @Override + protected CompletableFuture onTeamsMembersAdded( + List membersAdded, + TeamInfo teamInfo, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter(member -> !StringUtils.equals(member.getId(), turnContext.getActivity().getRecipient().getId())) + .map(channel -> turnContext.sendActivity( + MessageFactory.text("Welcome to the team " + channel.getGivenName() + " " + channel.getSurname() + "."))) + .collect(CompletableFutures.toFutureList()) + .thenApply(resourceResponses -> null); + } + + private CompletableFuture deleteCardActivity(TurnContext turnContext) { + return turnContext.deleteActivity(turnContext.getActivity().getReplyToId()); + } + + // If you encounter permission-related errors when sending this message, see + // https://aka.ms/BotTrustServiceUrl + private CompletableFuture messageAllMembers(TurnContext turnContext) { + String teamsChannelId = turnContext.getActivity().teamsGetChannelId(); + String serviceUrl = turnContext.getActivity().getServiceUrl(); + MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(appId, appPassword); + + return TeamsInfo.getMembers(turnContext) + .thenCompose(members -> { + List> conversations = new ArrayList<>(); + + // Send a message to each member. These will all go out + // at the same time. + for (TeamsChannelAccount member : members) { + Activity proactiveMessage = MessageFactory.text( + "Hello " + member.getGivenName() + " " + member.getSurname() + + ". I'm a Teams conversation bot."); + + ConversationParameters conversationParameters = new ConversationParameters() {{ + setIsGroup(false); + setBot(turnContext.getActivity().getRecipient()); + setMembers(Collections.singletonList(member)); + setTenantId(turnContext.getActivity().getConversation().getTenantId()); + }}; + + conversations.add( + ((BotFrameworkAdapter) turnContext.getAdapter()).createConversation( + teamsChannelId, + serviceUrl, + credentials, + conversationParameters, + (context) -> { + ConversationReference reference = context.getActivity().getConversationReference(); + return context.getAdapter().continueConversation( + appId, + reference, + (inner_context) -> inner_context.sendActivity(proactiveMessage) + .thenApply(resourceResponse -> null) + ); + } + ) + ); + } + + return CompletableFuture.allOf(conversations.toArray(new CompletableFuture[0])); + }) + // After all member messages are sent, send confirmation to the user. + .thenApply(conversations -> turnContext.sendActivity(MessageFactory.text("All messages have been sent."))) + .thenApply(allSent -> null); + } + + private CompletableFuture updateCardActivity(TurnContext turnContext) { + Map data = (Map) turnContext.getActivity().getValue(); + data.put("count", (int) data.get("count") + 1); + + HeroCard card = new HeroCard() {{ + setTitle("Welcome Card"); + setText("Update count - " + data.get("count")); + setButtons(Arrays.asList( + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Update Card"); + setText("UpdateCardAction"); + setValue(data); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Message All Members"); + setText("MessageAllMembers"); + }}, + new CardAction() {{ + setType(ActionTypes.MESSAGE_BACK); + setTitle("Delete card"); + setText("Delete"); + }} + )); + }}; + + Activity updatedActivity = MessageFactory.attachment(card.toAttachment()); + updatedActivity.setId(turnContext.getActivity().getReplyToId()); + + return turnContext.updateActivity(updatedActivity) + .thenApply(resourceResponse -> null); + } + + private CompletableFuture mentionActivity(TurnContext turnContext) { + Mention mention = new Mention(); + mention.setMentioned(turnContext.getActivity().getFrom()); + mention.setText("" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + ""); + + Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'"); + replyActivity.setMentions(Collections.singletonList(mention)); + + return turnContext.sendActivity(replyActivity) + .thenApply(resourceResponse -> null); + } +} diff --git a/samples/53.teams-messaging-extensions-action-preview/src/main/resources/application.properties b/samples/53.teams-messaging-extensions-action-preview/src/main/resources/application.properties new file mode 100644 index 000000000..a695b3bf0 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/src/main/resources/application.properties @@ -0,0 +1,2 @@ +MicrosoftAppId= +MicrosoftAppPassword= diff --git a/samples/53.teams-messaging-extensions-action-preview/src/main/resources/log4j2.json b/samples/53.teams-messaging-extensions-action-preview/src/main/resources/log4j2.json new file mode 100644 index 000000000..67c0ad530 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/src/main/webapp/META-INF/MANIFEST.MF b/samples/53.teams-messaging-extensions-action-preview/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/samples/53.teams-messaging-extensions-action-preview/src/main/webapp/WEB-INF/web.xml b/samples/53.teams-messaging-extensions-action-preview/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/src/main/webapp/index.html b/samples/53.teams-messaging-extensions-action-preview/src/main/webapp/index.html new file mode 100644 index 000000000..40b74c007 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/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/53.teams-messaging-extensions-action-preview/src/test/java/com/microsoft/bot/sample/teamsactionpreview/ApplicationTests.java b/samples/53.teams-messaging-extensions-action-preview/src/test/java/com/microsoft/bot/sample/teamsactionpreview/ApplicationTests.java new file mode 100644 index 000000000..68f70f1e7 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/src/test/java/com/microsoft/bot/sample/teamsactionpreview/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.sample.teamsactionpreview; + +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/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-color.png b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-color.png new file mode 100644 index 000000000..48a2de133 Binary files /dev/null and b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-color.png differ diff --git a/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-outline.png b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-outline.png new file mode 100644 index 000000000..dbfa92772 Binary files /dev/null and b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/icon-outline.png differ diff --git a/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/manifest.json b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/manifest.json new file mode 100644 index 000000000..8c87f9f40 --- /dev/null +++ b/samples/53.teams-messaging-extensions-action-preview/teamsAppManifest/manifest.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json", + "manifestVersion": "1.5", + "version": "1.0", + "id": "<>", + "packageName": "com.microsoft.teams.samples", + "developer": { + "name": "Microsoft", + "websiteUrl": "https://dev.botframework.com", + "privacyUrl": "https://privacy.microsoft.com", + "termsOfUseUrl": "https://www.microsoft.com/en-us/legal/intellectualproperty/copyright/default.aspx" + }, + "icons": { + "color": "icon-color.png", + "outline": "icon-outline.png" + }, + "name": { + "short": "Preview Messaging Extension", + "full": "Microsoft Teams Action Based Messaging Extension with Preview" + }, + "description": { + "short": "Sample demonstrating an Action Based Messaging Extension with Preview", + "full": "Sample Action Messaging Extension built with the Bot Builder SDK demonstrating Preview" + }, + "accentColor": "#FFFFFF", + "bots": [ + { + "botId": "<>", + "scopes": [ + "team" + ] + } + ], + "composeExtensions": [ + { + "botId": "<>", + "canUpdateConfiguration": false, + "commands": [ + { + "id": "createWithPreview", + "type": "action", + "title": "Create Card", + "description": "Example of creating a Card", + "initialRun": false, + "fetchTask": true, + "context": [ + "commandBox", + "compose", + "message" + ], + "parameters": [ + { + "name": "param", + "title": "param", + "description": "" + } + ] + } + ] + } + ], + "permissions": [ + "identity", + "messageTeamMembers" + ], + "validDomains": [] +} \ No newline at end of file