Merge pull request #424 from microsoft/emimunoz/messaging-ext-action
51 Teams Messaging Action Extension
This commit is contained in:
Коммит
0036294979
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче