π Search commands
Message extension search commands allow users to search external systems and insert the results of that search into a message in the form of a card.
Search command invocation locationsβ
There are three different areas search commands can be invoked from:
- Compose Area
- Compose Box
Compose Area and Boxβ
Setting up your Teams app manifestβ
To use search commands you have define them in the Teams app manifest. Here is an example:
"composeExtensions": [
{
"botId": "${{BOT_ID}}",
"commands": [
{
"id": "searchQuery",
"context": [
"compose",
"commandBox"
],
"description": "Test command to run query",
"title": "Search query",
"type": "query",
"parameters": [
{
"name": "searchQuery",
"title": "Search Query",
"description": "Your search query",
"inputType": "text"
}
]
}
]
}
]
Here we are defining the searchQuery
search (or query) command.
Handle submissionβ
Handle opening adaptive card dialog when the searchQuery
query is submitted.
@app.on_message_ext_query
async def handle_message_ext_query(ctx: ActivityContext[MessageExtensionQueryInvokeActivity]):
command_id = ctx.activity.value.command_id
search_query = ""
if ctx.activity.value.parameters and len(ctx.activity.value.parameters) > 0:
search_query = ctx.activity.value.parameters[0].value or ""
if command_id == "searchQuery":
cards = await create_dummy_cards(search_query)
attachments: list[MessagingExtensionAttachment] = []
for card_data in cards:
main_attachment = card_attachment(AdaptiveCardAttachment(content=card_data["card"]))
preview_attachment = card_attachment(ThumbnailCardAttachment(content=card_data["thumbnail"]))
attachment = MessagingExtensionAttachment(
content_type=main_attachment.content_type, content=main_attachment.content, preview=preview_attachment
)
attachments.append(attachment)
result = MessagingExtensionResult(
type=MessagingExtensionResultType.RESULT, attachment_layout=AttachmentLayout.LIST, attachments=attachments
)
return MessagingExtensionInvokeResponse(compose_extension=result)
return InvokeResponse[MessagingExtensionInvokeResponse](status=400)
create_dummy_cards()
method
async def create_dummy_cards(search_query: str) -> List[Dict[str, Any]]:
"""Create dummy cards for search results."""
dummy_items = [
{
"title": "Item 1",
"description": f"This is the first item and this is your search query: {search_query}",
},
{"title": "Item 2", "description": "This is the second item"},
{"title": "Item 3", "description": "This is the third item"},
{"title": "Item 4", "description": "This is the fourth item"},
{"title": "Item 5", "description": "This is the fifth item"},
]
cards: List[Dict[str, Any]] = []
for item in dummy_items:
card_data: Dict[str, Any] = {
"card": AdaptiveCard.model_validate(
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": item["title"],
"size": "Large",
"weight": "Bolder",
"color": "Accent",
"style": "heading",
},
{"type": "TextBlock", "text": item["description"], "wrap": True, "spacing": "Medium"},
],
}
),
"thumbnail": {
"title": item["title"],
"text": item["description"],
},
}
cards.append(card_data)
return cards
The search results include both a full adaptive card and a preview card. The preview card appears as a list item in the search command area:
When a user clicks on a list item the dummy adaptive card is added to the compose box:
To implement custom actions when a user clicks on a search result item, you can add the tap
property to the preview card. This allows you to handle the click event with custom logic:
@app.on_message_ext_select_item
async def handle_message_ext_select_item(ctx: ActivityContext[MessageExtensionSelectItemInvokeActivity]):
option = getattr(ctx.activity.value, "option", None)
await ctx.send(f"Selected item: {option}")
result = MessagingExtensionResult(
type=MessagingExtensionResultType.RESULT, attachment_layout=AttachmentLayout.LIST, attachments=[]
)
return MessagingExtensionInvokeResponse(compose_extension=result)