Skip to content

adding search extension scenario #509

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

Merged
merged 4 commits into from
Dec 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions scenarios/search-based-messaging-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# RosterBot

Bot Framework v4 teams roster bot sample.

This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back.

## Running the sample
- Clone the repository
```bash
git clone https://github.com/Microsoft/botbuilder-python.git
```
- Activate your desired virtual environment
- Bring up a terminal, navigate to `botbuilder-python\samples\roster` folder
- In the terminal, type `pip install -r requirements.txt`
- In the terminal, type `python app.py`

## Testing the bot using Bot Framework Emulator
[Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.

- Install the Bot Framework emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)

### Connect to bot using Bot Framework Emulator
- Launch Bot Framework Emulator
- Paste this URL in the emulator window - http://localhost:3978/api/messages

## Further reading

- [Bot Framework Documentation](https://docs.botframework.com)
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
83 changes: 83 additions & 0 deletions scenarios/search-based-messaging-extension/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import json
import sys
from datetime import datetime

from aiohttp import web
from aiohttp.web import Request, Response, json_response

from botbuilder.core import (
BotFrameworkAdapterSettings,
TurnContext,
BotFrameworkAdapter,
)

from botbuilder.schema import Activity, ActivityTypes
from bots import SearchBasedMessagingExtension
from config import DefaultConfig

CONFIG = DefaultConfig()

# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)
ADAPTER = BotFrameworkAdapter(SETTINGS)

# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log .vs. app insights.
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)

# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code."
)
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)

# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)

ADAPTER.on_turn_error = on_error

# Create the Bot
BOT = SearchBasedMessagingExtension()

# Listen for incoming requests on /api/messages
async def messages(req: Request) -> Response:
# Main bot message handler.
if "application/json" in req.headers["Content-Type"]:
body = await req.json()
else:
return Response(status=415)

activity = Activity().deserialize(body)
auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""

try:
response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
if response:
return json_response(data=response.body, status=response.status)
return Response(status=201)
except Exception as exception:
raise exception

APP = web.Application()
APP.router.add_post("/api/messages", messages)

if __name__ == "__main__":
try:
web.run_app(APP, host="localhost", port=CONFIG.PORT)
except Exception as error:
raise error
6 changes: 6 additions & 0 deletions scenarios/search-based-messaging-extension/bots/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .search_based_messaging_extension import SearchBasedMessagingExtension

__all__ = ["SearchBasedMessagingExtension"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from botbuilder.core import CardFactory, MessageFactory, TurnContext
from botbuilder.schema import ChannelAccount, ThumbnailCard, CardImage, HeroCard, Attachment, CardAction
from botbuilder.schema.teams import AppBasedLinkQuery, MessagingExtensionAttachment, MessagingExtensionQuery, MessagingExtensionResult, MessagingExtensionResponse
from botbuilder.core.teams import TeamsActivityHandler, TeamsInfo

from typing import List
import requests

class SearchBasedMessagingExtension(TeamsActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
await turn_context.send_activities(MessageFactory.text(f"Echo: {turn_context.activity.text}"))

async def on_teams_messaging_extension_query(self, turn_context: TurnContext, query: MessagingExtensionQuery):
search_query = str(query.parameters[0].value)
response = requests.get(f"http://registry.npmjs.com/-/v1/search",params={"text":search_query})
data = response.json()

attachments = []

for obj in data["objects"]:
hero_card = HeroCard(
title=obj["package"]["name"],
tap=CardAction(
type="invoke",
value=obj["package"]
),
preview=[CardImage(url=obj["package"]["links"]["npm"])]
)

attachment = MessagingExtensionAttachment(
content_type=CardFactory.content_types.hero_card,
content=HeroCard(title=obj["package"]["name"]),
preview=CardFactory.hero_card(hero_card)
)
attachments.append(attachment)
return MessagingExtensionResponse(
compose_extension=MessagingExtensionResult(
type="result",
attachment_layout="list",
attachments=attachments
)
)



async def on_teams_messaging_extension_select_item(self, turn_context: TurnContext, query) -> MessagingExtensionResponse:
hero_card = HeroCard(
title=query["name"],
subtitle=query["description"],
buttons=[
CardAction(
type="openUrl",
value=query["links"]["npm"]
)
]
)
attachment = MessagingExtensionAttachment(
content_type=CardFactory.content_types.hero_card,
content=hero_card
)

return MessagingExtensionResponse(
compose_extension=MessagingExtensionResult(
type="result",
attachment_layout="list",
attachments=[attachment]
)
)

def _create_messaging_extension_result(self, attachments: List[MessagingExtensionAttachment]) -> MessagingExtensionResult:
return MessagingExtensionResult(
type="result",
attachment_layout="list",
attachments=attachments
)

def _create_search_result_attachment(self, search_query: str) -> MessagingExtensionAttachment:
card_text = f"You said {search_query}"
bf_logo = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU"

button = CardAction(
type="openUrl",
title="Click for more Information",
value="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/bots/bots-overview"
)

images = [CardImage(url=bf_logo)]
buttons = [button]

hero_card = HeroCard(
title="You searched for:",
text=card_text,
images=images,
buttons=buttons
)

return MessagingExtensionAttachment(
content_type=CardFactory.content_types.hero_card,
content=hero_card,
preview=CardFactory.hero_card(hero_card)
)

def _create_dummy_search_result_attachment(self) -> MessagingExtensionAttachment:
card_text = "https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/bots/bots-overview"
bf_logo = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU"

button = CardAction(
type = "openUrl",
title = "Click for more Information",
value = "https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/bots/bots-overview"
)

images = [CardImage(url=bf_logo)]

buttons = [button]


hero_card = HeroCard(
title="Learn more about Teams:",
text=card_text, images=images,
buttons=buttons
)

preview = HeroCard(
title="Learn more about Teams:",
text=card_text,
images=images
)

return MessagingExtensionAttachment(
content_type = CardFactory.content_types.hero_card,
content = hero_card,
preview = CardFactory.hero_card(preview)
)

def _create_select_items_result_attachment(self, search_query: str) -> MessagingExtensionAttachment:
card_text = f"You said {search_query}"
bf_logo = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU"

buttons = CardAction(
type="openUrl",
title="Click for more Information",
value="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/bots/bots-overview"
)

images = [CardImage(url=bf_logo)]
buttons = [buttons]

select_item_tap = CardAction(
type="invoke",
value={"query": search_query}
)

hero_card = HeroCard(
title="You searched for:",
text=card_text,
images=images,
buttons=buttons
)

preview = HeroCard(
title=card_text,
text=card_text,
images=images,
tap=select_item_tap
)

return MessagingExtensionAttachment(
content_type=CardFactory.content_types.hero_card,
content=hero_card,
preview=CardFactory.hero_card(preview)
)
13 changes: 13 additions & 0 deletions scenarios/search-based-messaging-extension/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import os


class DefaultConfig:
""" Bot Configuration """

PORT = 3978
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
2 changes: 2 additions & 0 deletions scenarios/search-based-messaging-extension/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
botbuilder-core>=4.4.0b1
flask>=1.0.3
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "1.0",
"id": "<<YOUR-MICROSOFT-BOT-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": "<<YOUR-MICROSOFT-BOT-ID>>",
"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"
}
]
}
]
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.