Merge pull request #424 from microsoft/emimunoz/messaging-ext-action

51 Teams Messaging Action Extension
This commit is contained in:
tracyboehrer 2020-04-23 09:01:25 -05:00 коммит произвёл GitHub
Родитель c5c63b9a49 ee312158c8
Коммит 0036294979
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 159 добавлений и 269 удалений

Просмотреть файл

@ -3,8 +3,8 @@
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.
There are two basic types of Messaging Extension in Teams: [Search-based](https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/search-commands/define-search-command) and [Action-based](https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/action-commands/define-action-command). This sample illustrates how to
build an Action-based Messaging Extension.
## Prerequisites
@ -48,23 +48,13 @@ the Teams service needs to call into the bot.
## Interacting with the bot
You can interact with this bot by sending it a message, or selecting a command from the command list. The bot will respond to the following strings.
> Note this `manifest.json` specified that the bot will be called from both the `compose` and `message` areas of Teams. Please refer to Teams documentation for more details.
1. **Show Welcome**
- **Result:** The bot will send the welcome card for you to interact with
- **Valid Scopes:** personal, group chat, team chat
2. **MentionMe**
- **Result:** The bot will respond to the message and mention the user
- **Valid Scopes:** personal, group chat, team chat
3. **MessageAllMembers**
- **Result:** The bot will send a 1-on-1 message to each member in the current conversation (aka on the conversation's roster).
- **Valid Scopes:** personal, group chat, team chat
1) Selecting the **Create Card** command from the Compose Box command list. The parameters dialog will be displayed and can be submitted to initiate the card creation within the Messaging Extension code.
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.
or
### 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.
2) Selecting the **Share Message** command from the Message command list.
## Deploy the bot to Azure

Просмотреть файл

@ -14,11 +14,11 @@ import org.springframework.context.annotation.Import;
/**
* This is the starting point of the Sprint Boot Bot application.
* <p>
* This class also provides overrides for dependency injections. A class that extends the {@link
* com.microsoft.bot.builder.Bot} interface should be annotated with @Component.
*
* This class also provides overrides for dependency injections. A class that extends the
* {@link com.microsoft.bot.builder.Bot} interface should be annotated with @Component.
*
* @see TeamsConversationBot
* @see TeamsMessagingExtensionsActionBot
*/
@SpringBootApplication
@ -29,6 +29,7 @@ import org.springframework.context.annotation.Import;
@Import({BotController.class})
public class Application extends BotDependencyConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

Просмотреть файл

@ -3,210 +3,109 @@
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.CardImage;
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 com.microsoft.bot.schema.teams.*;
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.*;
import java.util.concurrent.CompletableFuture;
/**
* This class implements the functionality of the Bot.
*
* <p>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.</p>
* added. There are two basic types of Messaging Extension in Teams: Search-based and Action-based.
* This sample illustrates how to build an Action-based Messaging Extension.</p>
*/
@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<Void> 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);
protected CompletableFuture<MessagingExtensionActionResponse> onTeamsMessagingExtensionSubmitAction(
TurnContext turnContext,
MessagingExtensionAction action
) {
switch (action.getCommandId()) {
// These commandIds are defined in the Teams App Manifest.
case "createCard":
return createCardCommand(turnContext, action);
case "shareMessage":
return shareMessageCommand(turnContext, action);
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);
return notImplemented(
String.format("Invalid CommandId: %s", action.getCommandId()));
}
}
@Override
protected CompletableFuture<Void> onTeamsMembersAdded(
List<TeamsChannelAccount> membersAdded,
TeamInfo teamInfo,
TurnContext turnContext
private CompletableFuture<MessagingExtensionActionResponse> createCardCommand(
TurnContext turnContext,
MessagingExtensionAction action
) {
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<Void> 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<Void> 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<CompletableFuture<Void>> 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<Void> updateCardActivity(TurnContext turnContext) {
Map data = (Map) turnContext.getActivity().getValue();
data.put("count", (int) data.get("count") + 1);
Map<String, String> actionData = (Map<String, String>) action.getData();
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");
}}
));
setTitle(actionData.get("title"));
setSubtitle(actionData.get("subTitle"));
setText(actionData.get("text"));
}};
Activity updatedActivity = MessageFactory.attachment(card.toAttachment());
updatedActivity.setId(turnContext.getActivity().getReplyToId());
List<MessagingExtensionAttachment> attachments = Arrays
.asList(new MessagingExtensionAttachment() {{
setContent(card);
setContentType(HeroCard.CONTENTTYPE);
setPreview(card.toAttachment());
}});
return turnContext.updateActivity(updatedActivity)
.thenApply(resourceResponse -> null);
return CompletableFuture.completedFuture(new MessagingExtensionActionResponse() {{
setComposeExtension(new MessagingExtensionResult() {{
setAttachments(attachments);
setAttachmentLayout("list");
setType("result");
}});
}});
}
private CompletableFuture<Void> mentionActivity(TurnContext turnContext) {
Mention mention = new Mention();
mention.setMentioned(turnContext.getActivity().getFrom());
mention.setText("<at>" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + "</at>");
private CompletableFuture<MessagingExtensionActionResponse> shareMessageCommand(
TurnContext turnContext,
MessagingExtensionAction action
) {
Map<String, String> actionData = (Map<String, String>) action.getData();
Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'");
replyActivity.setMentions(Collections.singletonList(mention));
HeroCard card = new HeroCard() {{
setTitle(
action.getMessagePayload().getFrom().getUser() != null ? action.getMessagePayload()
.getFrom().getUser().getDisplayName() : "");
setText(action.getMessagePayload().getBody().getContent());
}};
return turnContext.sendActivity(replyActivity)
.thenApply(resourceResponse -> null);
if (action.getMessagePayload().getAttachments() != null && !action.getMessagePayload()
.getAttachments().isEmpty()) {
card.setSubtitle("Attachments not included)");
}
boolean includeImage = actionData.get("includeImage") != null ? (
Boolean.valueOf(actionData.get("includeImage"))
) : false;
if (includeImage) {
card.setImages(Arrays.asList(new CardImage() {{
setUrl(
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU");
}}));
}
return CompletableFuture.completedFuture(new MessagingExtensionActionResponse() {{
setComposeExtension(new MessagingExtensionResult() {{
setAttachmentLayout("list");
setType("result");
setAttachments(Arrays.asList(new MessagingExtensionAttachment() {{
setContent(card);
setContentType(HeroCard.CONTENTTYPE);
setPreview(card.toAttachment());
}}));
}});
}});
}
}

Просмотреть файл

@ -1,78 +1,78 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "1.0",
"id": "<<YOUR-MICROSOFT-APP-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": "<<YOUR-MICROSOFT-APP-ID>>",
"commands": [
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "1.0",
"id": "<<YOUR-MICROSOFT-APP-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": [
{
"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"
}
]
"botId": "<<YOUR-MICROSOFT-APP-ID>>",
"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"
]
],
"permissions": [
"identity"
]
}