-
Notifications
You must be signed in to change notification settings - Fork 539
Update samples and Teams SDK related changes in define-search-command.md #14241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Pranjal-MSFT
wants to merge
9
commits into
main
Choose a base branch
from
pranjalb-teamssdk-sample-code-updation-4
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+132
−119
Open
Changes from 5 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3b9ab4e
Update define-search-command.md
Pranjal-MSFT b0e0c67
Merge branch 'main' into pranjalb-teamssdk-sample-code-updation-4
Pranjal-MSFT ebb2e77
Update define-search-command.md
Pranjal-MSFT efe7141
Merge branch 'pranjalb-teamssdk-sample-code-updation-4' of https://gi…
Pranjal-MSFT d134baf
Update define-search-command.md
Pranjal-MSFT adfb5da
Update define-search-command.md
Pranjal-MSFT 678b285
Update define-search-command.md
Pranjal-MSFT 5005b2c
Update define-search-command.md
Pranjal-MSFT f20cf85
Update define-search-command.md
Pranjal-MSFT File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| --- | ||
| title: Create Search Commands for App | ||
| author: vikasalmal | ||
| description: Learn about message extension search commands for Teams apps, to create a search command through app manifest and manually. | ||
| ms.topic: article | ||
| ms.author: anclear | ||
| ms.date: 09/16/2024 | ||
| ms.date: 04/02/2026 | ||
| ms.localizationpriority: medium | ||
| ms.owner: slamba | ||
| --- | ||
|
|
@@ -26,7 +27,7 @@ The following image displays the invoke locations of the search command: | |
|
|
||
| To add the search command to your [app manifest](/microsoft-365/extensibility/schema/root-compose-extensions-commands) (previously called Teams app manifest), you must add a new `composeExtensions` object to the top level of your app manifest JSON. You can add the search command either with the help of Developer Portal or manually. | ||
|
|
||
| ### Create search message extension using Bot Framework | ||
| ### Create search message extension using Teams SDK | ||
|
|
||
| You can create a search message extension using Microsoft 365 Agents Toolkit (previously known as Teams Toolkit) and Developer Portal for Teams. | ||
|
|
||
|
|
@@ -45,12 +46,11 @@ To create a search-based message extension using Agents Toolkit, follow these st | |
|
|
||
| 1. Open **Visual Studio Code**. | ||
| 1. From the left pane, select **Microsoft 365 Agents Toolkit**. | ||
| 1. Select **Create a New Agents/App** > **Teams App**. | ||
| 1. Select **Create a New Agents/App** > **Teams Agents and Apps** > **Other Teams Capabilities**. | ||
| 1. Select **Message Extension**. | ||
| 1. Select **Custom Search Results**. | ||
| 1. Select a **programming language**. | ||
| 1. Select **Default folder**. | ||
| 1. Enter the name of your app and select **Enter**. | ||
| 1. Enter the name of your app/agent and select **Enter**. | ||
|
|
||
| Agents Toolkit scaffolds your project and creates a search message extension. | ||
|
|
||
|
|
@@ -106,7 +106,7 @@ To run the message extension in Teams, follow these steps: | |
| * Parameter description | ||
| * Select the type of input | ||
|
|
||
| 1. Select **Save**. A search message extension using bot framework created. | ||
| 1. Select **Save**. A search message extension is created. | ||
| 1. At the upper-right corner, select **Preview in Teams**. The app opens in Teams desktop or web client. | ||
|
|
||
| --- | ||
|
|
@@ -124,136 +124,149 @@ The following code provides an example of search-based for message extensions: | |
|
|
||
| # [.NET](#tab/dotnet) | ||
|
|
||
| * [SDK reference](/dotnet/api/microsoft.bot.builder.teams.teamsactivityhandler.onteamsmessagingextensionqueryasync?view=botbuilder-dotnet-stable&preserve-view=true) | ||
| * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs#L26-L59) | ||
| * [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=csharp) | ||
| * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/dotnet/bot-message-extensions/Program.cs) | ||
|
|
||
| ```csharp | ||
| protected override async Task<MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken) | ||
| { | ||
| var text = query?.Parameters?[0]?.Value as string ?? string.Empty; | ||
|
|
||
| var packages = new[] { | ||
| new { title = "A very extensive set of extension methods", value = "FluentAssertions" }, | ||
| new { title = "Fluent UI Library", value = "FluentUI" }}; | ||
|
|
||
| // We take every row of the results and wrap them in cards wrapped in MessagingExtensionAttachment objects. | ||
| // The Preview is optional, if it includes a Tap, that will trigger the OnTeamsMessagingExtensionSelectItemAsync event back on this bot. | ||
| var attachments = packages.Select(package => | ||
| { | ||
| var previewCard = new ThumbnailCard { Title = package.title, Tap = new CardAction { Type = "invoke", Value = package } }; | ||
| if (!string.IsNullOrEmpty(package.title)) | ||
| { | ||
| previewCard.Images = new List<CardImage>() { new CardImage(package.title, "Icon") }; | ||
| } | ||
|
|
||
| var attachment = new MessagingExtensionAttachment | ||
| { | ||
| ContentType = HeroCard.ContentType, | ||
| Content = new HeroCard { Title = package.title }, | ||
| Preview = previewCard.ToAttachment() | ||
| }; | ||
|
|
||
| return attachment; | ||
| }).ToList(); | ||
|
|
||
| // The list of MessagingExtensionAttachments must we wrapped in a MessagingExtensionResult wrapped in a MessagingExtensionResponse. | ||
| return new MessagingExtensionResponse | ||
| { | ||
| ComposeExtension = new MessagingExtensionResult | ||
| { | ||
| Type = "result", | ||
| AttachmentLayout = "list", | ||
| Attachments = attachments | ||
| } | ||
| }; | ||
| } | ||
| teams.OnQuery(async (ctx) => | ||
| { | ||
| var commandId = ctx.Activity.Value.CommandId; | ||
| var parameters = ctx.Activity.Value.Parameters; | ||
| var query = parameters?.FirstOrDefault()?.Value?.ToString() ?? ""; | ||
|
|
||
| Console.WriteLine($"Query: command={commandId}, query={query}"); | ||
|
|
||
| var attachments = new List<MsgExt.Attachment>(); | ||
|
|
||
| // Route to appropriate search | ||
| if (commandId == "wikipediaSearch") | ||
| { | ||
| var results = await SearchWikipedia(query); | ||
| attachments = results.Select(r => | ||
| { | ||
| var title = r["title"]?.ToString() ?? "No Title"; | ||
| var snippet = Regex.Replace(r["snippet"]?.ToString() ?? "", "<[^>]+>", ""); | ||
| return CreateAttachment(CreateWikipediaCard(r), title, snippet); | ||
| }).ToList(); | ||
| } | ||
|
|
||
| if (attachments.Count == 0) | ||
| { | ||
| return new MsgExt.Response | ||
| { | ||
| ComposeExtension = new MsgExt.Result | ||
| { | ||
| Type = MsgExt.ResultType.Message, | ||
| Text = $"No results found for '{query}'" | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| return new MsgExt.Response | ||
| { | ||
| ComposeExtension = new MsgExt.Result | ||
| { | ||
| Type = MsgExt.ResultType.Result, | ||
| AttachmentLayout = Attachment.Layout.List, | ||
| Attachments = attachments | ||
| } | ||
| }; | ||
| }); | ||
| ``` | ||
|
|
||
| # [Node.js](#tab/nodejs) | ||
|
|
||
| [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/msgext-search-quickstart/js/botActivityHandler.js#L30-L53) | ||
|
|
||
| ```javascript | ||
| async handleTeamsMessagingExtensionQuery(context, query) { | ||
| const searchQuery = query.parameters[0].value; | ||
| const attachments = []; | ||
| const response = await axios.get(`http://registry.npmjs.com/-/v1/search?${ querystring.stringify({ text: searchQuery, size: 8 }) }`); | ||
|
|
||
| response.data.objects.forEach(obj => { | ||
| const heroCard = CardFactory.heroCard(obj.package.name); | ||
| const preview = CardFactory.heroCard(obj.package.name); | ||
| preview.content.tap = { type: 'invoke', value: { description: obj.package.description } }; | ||
| const attachment = { ...heroCard, preview }; | ||
| attachments.push(attachment); | ||
| }); | ||
|
|
||
| return { | ||
| composeExtension: { | ||
| type: 'result', | ||
| attachmentLayout: 'list', | ||
| attachments: attachments | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| * [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=typescript) | ||
| * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/nodejs/bot-message-extensions/index.ts) | ||
|
|
||
| ```TypeScript | ||
| app.on('message.ext.query', async ({ activity }) => { | ||
| const commandId = activity.value?.commandId | ||
| const params = activity.value?.parameters || [] | ||
| const query = params[0]?.value || '' | ||
| console.log(`Query: command=${commandId}, query=${query}`) | ||
| let attachments: MessagingExtensionAttachment[] = [] | ||
|
|
||
| if (commandId === 'wikipediaSearch') { | ||
| const results = await searchWikipedia(query) | ||
| attachments = results.map((r: any) => | ||
| createAttachment( | ||
| createWikipediaCard(r), | ||
| r.title, | ||
| (r.snippet || '').replace(/<[^>]+>/g, '') | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| if (!attachments.length) { | ||
| return { | ||
| composeExtension: { | ||
| type: 'message', | ||
| text: `No results found for '${query}'`, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| composeExtension: { | ||
| type: 'result', | ||
| attachmentLayout: 'list', | ||
| attachments, | ||
| }, | ||
| } | ||
| }) | ||
| ``` | ||
|
|
||
| # [Python](#tab/python) | ||
|
|
||
| * [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=python) | ||
| * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/python/bot-message-extensions/main.py) | ||
|
|
||
| ```python | ||
| async def on_teams_messaging_extension_query(self, context, query): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the code snippet @app.on_message_ext_query |
||
| """ | ||
| Handles Messaging Extension queries in Teams. | ||
| @app.on_message_ext_query | ||
| async def handle_query(ctx: ActivityContext[MessageExtensionQueryInvokeActivity]): | ||
| command_id = ctx.activity.value.command_id | ||
| params = ctx.activity.value.parameters or [] | ||
| query = params[0].value if params else "" | ||
|
|
||
| print(f"Query: command={command_id}, query={query}") | ||
|
|
||
| # Route to appropriate search | ||
| if command_id == "wikipediaSearch": | ||
| results = await search_wikipedia(query) | ||
| attachments = [ | ||
| create_attachment( | ||
| create_wikipedia_card(r), | ||
| r['title'], | ||
| re.sub(r'<[^>]+>', '', r.get('snippet', '')) | ||
| ) | ||
| for r in results | ||
| ] | ||
|
|
||
| if not attachments: | ||
| return MessagingExtensionInvokeResponse( | ||
| compose_extension=MessagingExtensionResult( | ||
| type=MessagingExtensionResultType.MESSAGE, | ||
| text=f"No results found for '{query}'" | ||
| ) | ||
| ) | ||
|
|
||
| This method generates a list of thumbnail cards containing random text and images when the "getRandomText" command is triggered. It creates preview cards with tap actions and returns them as a Messaging Extension response. | ||
| """ | ||
| faker = Faker() | ||
| title = query.command_id | ||
| random_image_url = "https://loremflickr.com/200/200" | ||
|
|
||
| if query.command_id == "getRandomText": | ||
| attachments = [] | ||
| # Generate 5 results with fake text and fake images | ||
| for i in range(5): | ||
| text = faker.paragraph() | ||
| images = [f"{random_image_url}?random={i}"] | ||
|
|
||
| # Create a thumbnail card using ThumbnailCard | ||
| thumbnail_card = self.create_thumbnail_card(title, text, images) | ||
|
|
||
| # Create a preview card and add the tap action | ||
| preview_card = self.create_thumbnail_card(title, text, images) | ||
| tap_action = CardAction( | ||
| type="invoke", | ||
| value={"title": title, "text": text, "images": images}, | ||
| ) | ||
| preview_attachment = CardFactory.thumbnail_card(preview_card) | ||
| preview_attachment.content.tap = tap_action | ||
|
|
||
| # Combine the thumbnail card and the preview | ||
| attachment = MessagingExtensionAttachment( | ||
| content = thumbnail_card, | ||
| content_type=CardFactory.content_types.thumbnail_card, | ||
| preview=preview_attachment, | ||
| ) | ||
| attachments.append(attachment) | ||
|
|
||
| return MessagingExtensionResponse( | ||
| compose_extension=MessagingExtensionResult( | ||
| type="result", | ||
| attachment_layout="list", | ||
| attachments=attachments, | ||
| ) | ||
| ) | ||
| return MessagingExtensionInvokeResponse( | ||
| compose_extension=MessagingExtensionResult( | ||
| type=MessagingExtensionResultType.RESULT, | ||
| attachment_layout=AttachmentLayout.LIST, | ||
| attachments=attachments | ||
| ) | ||
| ) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Code sample | ||
|
|
||
| | Sample name | Description | .NET | Node.js | Manifest| | ||
| | Sample name | Description | .NET | Node.js | Python| | ||
| |:---------------------|:--------------|:---------|:--------|:--------------| | ||
| |Teams message extension search | This sample demonstrates how to create a Messaging Extension in Microsoft Teams that allows users to perform searches and retrieve results. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/csharp)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/nodejs)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/csharp/demo-manifest/msgext-search.zip) | ||
| |Bot Message Extensions | This sample demonstrates a search-based messaging extension in Microsoft Teams that allows users to search for Wikipedia articles. The extension supports search commands, item selection, and link unfurling. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/dotnet/bot-message-extensions)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/nodejs/bot-message-extensions)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/python/bot-message-extensions) | ||
|
|
||
| ## Next step | ||
|
|
||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.