diff --git a/samples/50.teams-messaging-extensions-search/README.md b/samples/50.teams-messaging-extensions-search/README.md
index 6dd585698..6f89349c4 100644
--- a/samples/50.teams-messaging-extensions-search/README.md
+++ b/samples/50.teams-messaging-extensions-search/README.md
@@ -1,10 +1,11 @@
# 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.
+[Messaging Extensions](https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/what-are-messaging-extensions) are a special kind of Microsoft Teams application that is support by the [Bot Framework](https://dev.botframework.com) v4.
+
+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 a Search-based Messaging Extension.
## Prerequisites
@@ -48,19 +49,11 @@ 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 feature will be available from both the `compose` and `commandBox` 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
+In Teams, the command bar is located at the top of the window. When you at mention the bot what you type is forwarded (as you type) to the bot for processing. By way of illustration, this sample uses the text it receives to query the NuGet package store.
-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.
+There is a secondary, drill down, event illustrated in this sample: clicking on the results from the initial query will result in the bot receiving another event.
### Avoiding Permission-Related Errors
diff --git a/samples/50.teams-messaging-extensions-search/pom.xml b/samples/50.teams-messaging-extensions-search/pom.xml
index 5f3e94027..5d28a1ff8 100644
--- a/samples/50.teams-messaging-extensions-search/pom.xml
+++ b/samples/50.teams-messaging-extensions-search/pom.xml
@@ -70,6 +70,11 @@
log4j-api
2.11.0
+
+ org.json
+ json
+ 20190722
+
org.apache.logging.log4j
log4j-core
@@ -82,7 +87,17 @@
4.0.0-SNAPSHOT
compile
-
+
+ com.squareup.okhttp3
+ okhttp
+ 3.12.2
+
+
+ org.apache.commons
+ commons-lang3
+ 3.10
+
+
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
index c52248fad..c10fc2f92 100644
--- 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
@@ -14,11 +14,11 @@
/**
* 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
+ * @see TeamsMessagingExtensionsSearchBot
*/
@SpringBootApplication
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
index c45f4922c..f5efe9ad3 100644
--- 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
@@ -3,210 +3,157 @@
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 com.microsoft.bot.schema.*;
+import com.microsoft.bot.schema.teams.*;
+import java.util.concurrent.CompletionException;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.slf4j.LoggerFactory;
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.io.IOException;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
/**
* This class implements the functionality of the Bot.
*
*
This is where application specific logic for interacting with the users would be
- * added. For this sample, the {@link #onMessageActivity(TurnContext)} echos the text
- * back to the user. The {@link #onMembersAdded(List, TurnContext)} will send a greeting
- * to new conversation participants.
+ * added. This sample illustrates how to build a Search-based Messaging Extension.
*/
+
@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
+ protected CompletableFuture onTeamsMessagingExtensionQuery(
+ TurnContext turnContext,
+ MessagingExtensionQuery query
) {
- 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);
- }
+ List queryParams = query.getParameters();
+ String text = "";
+ if (queryParams != null && !queryParams.isEmpty()) {
+ text = (String) queryParams.get(0).getValue();
+ }
- private CompletableFuture deleteCardActivity(TurnContext turnContext) {
- return turnContext.deleteActivity(turnContext.getActivity().getReplyToId());
- }
+ return findPackages(text)
+ .thenApply(packages -> {
+ List attachments = new ArrayList<>();
+ for (String[] item : packages) {
+ ThumbnailCard previewCard = new ThumbnailCard() {{
+ setTitle(item[0]);
+ setTap(new CardAction() {{
+ setType(ActionTypes.INVOKE);
+ setValue(new JSONObject().put("data", item).toString());
+ }});
+ }};
- // 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());
+ if (!StringUtils.isEmpty(item[4])) {
+ previewCard.setImages(Collections.singletonList(new CardImage() {{
+ setUrl(item[4]);
+ setAlt("Icon");
+ }}));
+ }
+
+ MessagingExtensionAttachment attachment = new MessagingExtensionAttachment() {{
+ setContentType(HeroCard.CONTENTTYPE);
+ setContent(new HeroCard() {{
+ setTitle(item[0]);
+ }});
+ setPreview(previewCard.toAttachment());
}};
- 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)
- );
- }
- )
- );
+ attachments.add(attachment);
}
- 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);
+ MessagingExtensionResult composeExtension = new MessagingExtensionResult() {{
+ setType("result");
+ setAttachmentLayout("list");
+ setAttachments(attachments);
+ }};
+
+ return new MessagingExtensionResponse(composeExtension);
+ });
}
- 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");
- }}
- ));
- }};
+ @Override
+ protected CompletableFuture onTeamsMessagingExtensionSelectItem(
+ TurnContext turnContext,
+ Object query
+ ) {
- Activity updatedActivity = MessageFactory.attachment(card.toAttachment());
- updatedActivity.setId(turnContext.getActivity().getReplyToId());
+ Map cardValue = (Map) query;
+ List data = (ArrayList) cardValue.get("data");
+ ThumbnailCard card = new ThumbnailCard() {{
+ setTitle(data.get(0));
+ setSubtitle(data.get(2));
+ setButtons(Arrays.asList(new CardAction() {{
+ setType(ActionTypes.OPEN_URL);
+ setTitle("Project");
+ setValue(data.get(3));
+ }}));
+ }};
- return turnContext.updateActivity(updatedActivity)
- .thenApply(resourceResponse -> null);
- }
+ if (!StringUtils.isEmpty(data.get(4))) {
+ card.setImages(Collections.singletonList(new CardImage() {{
+ setUrl(data.get(4));
+ setAlt("Icon");
+ }}));
+ }
- private CompletableFuture mentionActivity(TurnContext turnContext) {
- Mention mention = new Mention();
- mention.setMentioned(turnContext.getActivity().getFrom());
- mention.setText("" + URLEncoder.encode(turnContext.getActivity().getFrom().getName()) + "");
+ MessagingExtensionAttachment attachment = new MessagingExtensionAttachment() {{
+ setContentType(ThumbnailCard.CONTENTTYPE);
+ setContent(card);
+ }};
- Activity replyActivity = MessageFactory.text("Hello " + mention.getText() + ".'");
- replyActivity.setMentions(Collections.singletonList(mention));
+ MessagingExtensionResult composeExtension = new MessagingExtensionResult() {{
+ setType("result");
+ setAttachmentLayout("list");
+ setAttachments(Collections.singletonList(attachment));
+ }};
+ return CompletableFuture.completedFuture(new MessagingExtensionResponse(composeExtension));
+ }
- return turnContext.sendActivity(replyActivity)
- .thenApply(resourceResponse -> null);
+ private CompletableFuture> findPackages(String text) {
+ return CompletableFuture.supplyAsync(() -> {
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(String
+ .format(
+ "https://azuresearch-usnc.nuget.org/query?q=id:%s&prerelease=true",
+ text
+ ))
+ .build();
+
+ List filteredItems = new ArrayList();
+ try {
+ Response response = client.newCall(request).execute();
+ JSONObject obj = new JSONObject(response.body().string());
+ JSONArray dataArray = (JSONArray) obj.get("data");
+
+ dataArray.forEach(i -> {
+ JSONObject item = (JSONObject) i;
+ filteredItems.add(new String[]{
+ item.getString("id"),
+ item.getString("version"),
+ item.getString("description"),
+ item.has("projectUrl") ? item.getString("projectUrl") : "",
+ item.has("iconUrl") ? item.getString("iconUrl") : ""
+ });
+ });
+
+ } catch (IOException e) {
+ LoggerFactory.getLogger(TeamsMessagingExtensionsSearchBot.class)
+ .error("findPackages", e);
+ throw new CompletionException(e);
+ }
+ return filteredItems;
+ });
}
}
diff --git a/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json b/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json
index cd4900030..f153888e8 100644
--- a/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json
+++ b/samples/50.teams-messaging-extensions-search/teamsAppManifest/manifest.json
@@ -1,49 +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": [
+ "$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": [
{
- "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"
- }
- ]
+ "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
+ ]
+}