8000 adding mentions bot and fixing bug for resource response IDs · rusty0209/botbuilder-python@2e4d8c9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2e4d8c9

Browse files
committed
adding mentions bot and fixing bug for resource response IDs
1 parent a590910 commit 2e4d8c9

File tree

12 files changed

+274
-4
lines changed

12 files changed

+274
-4
lines changed

libraries/botbuilder-core/botbuilder/core/bot_framework_adapter.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
ConversationParameters,
1414
ConversationReference,
1515
TokenResponse,
16+
ResourceResponse,
1617
)
1718
from botframework.connector import Channels, EmulatorApiClient
1819
from botframework.connector.aio import ConnectorClient
@@ -330,9 +331,12 @@ async def delete_activity(
330331
except Exception as error:
331332
raise error
332333

333-
async def send_activities(self, context: TurnContext, activities: List[Activity]):
334+
async def send_activities(self, context: TurnContext, activities: List[Activity]) -> List[ResourceResponse]:
334335
try:
336+
responses = []
337+
335338
for activity in activities:
339+
response = ResourceResponse()
336340
if activity.type == "delay":
337341
try:
338342
delay_in_ms = float(activity.value) / 1000
@@ -348,14 +352,20 @@ async def send_activities(self, context: TurnContext, activities: List[Activity]
348352
context.turn_state.add(self._INVOKE_RESPONSE_KEY)
349353
elif activity.reply_to_id:
350354
client = self.create_connector_client(activity.service_url)
351-
await client.conversations.reply_to_activity(
355+
response = await client.conversations.reply_to_activity(
352356
activity.conversation.id, activity.reply_to_id, activity
353357
)
354358
else:
355359
client = self.create_connector_client(activity.service_url)
356-
await client.conversations.send_to_conversation(
360+
response = await client.conversations.send_to_conversation(
357361
activity.conversation.id, activity
358362
)
363+
364+
if not response:
365+
response.id = activity_id if activity_id else ""
366+
367+
responses.append(response)
368+
return responses
359369
except Exception as error:
360370
raise error
361371

libraries/botbuilder-core/botbuilder/core/turn_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ async def send_activity(
159159
)
160160
for a in activity_or_text
161161
]
162+
162163
for activity in output:
163164
if not activity.input_hint:
164165
activity.input_hint = "acceptingInput"
@@ -171,7 +172,6 @@ async def callback(context: "TurnContext", output):
171172
result = await self._emit(
172173
self._on_send_activities, output, callback(self, output)
173174
)
174-
175175
return result[0] if result else ResourceResponse()
176176

177177
async def update_activity(self, activity: Activity):
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# EchoBot
2+
3+
Bot Framework v4 echo bot sample.
4+
5+
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.
6+
7+
## Running the sample
8+
- Clone the repository
9+
```bash
10+
git clone https://github.com/Microsoft/botbuilder-python.git
11+
```
12+
- Activate your desired virtual environment
13+
- Bring up a terminal, navigate to `botbuilder-python\samples\02.echo-bot` folder
14+
- In the terminal, type `pip install -r requirements.txt`
15+
- In the terminal, type `python app.py`
16+
17+
## Testing the bot using Bot Framework Emulator
18+
[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.
19+
20+
- Install the Bot Framework emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
21+
22+
### Connect to bot using Bot Framework Emulator
23+
- Launch Bot Framework Emulator
24+
- Paste this URL in the emulator window - http://localhost:3978/api/messages
25+
26+
## Further reading
27+
28+
- [Bot Framework Documentation](https://docs.botframework.com)
29+
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
30+
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
from botbuilder.core import MemoryStorage
3+
from botbuilder.schema import Activity
4+
5+
6+
class ActivityLog():
7+
def __init__(self, storage: MemoryStorage):
8+
self._storage = storage
9+
10+
async def append(self, activity_id: str, activity: Activity):
11+
if not activity_id:
12+
raise TypeError("activity_id is required for ActivityLog.append")
13+
14+
if not activity:
15+
raise TypeError("activity is required for ActivityLog.append")
16+
17+
obj = {}
18+
obj[activity_id] = activity
19+
20+
await self._storage.write(obj)
21+
return
22+
23+
async def find(self, activity_id: str) -> Activity:
24+
if not activity_id:
25+
raise TypeError("activity_id is required for ActivityLog.find")
26+
27+
items = await self._storage.read([activity_id])
28+
return items[activity_id] if len(items) >= 1 else None
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import asyncio
5+
import sys
6+
from datetime import datetime
7+
from types import MethodType
8+
9+
from flask import Flask, request, Response
10+
from botbuilder.core import (
11+
BotFrameworkAdapterSettings,
12+
TurnContext,
13+
BotFrameworkAdapter,
14+
MemoryStorage
15+
)
16+
from botbuilder.schema import Activity, ActivityTypes
17+
from activity_log import ActivityLog
18+
from bots import MessageReactionBot
19+
20+
# Create the loop and Flask app
21+
LOOP = asyncio.get_event_loop()
22+
APP = Flask(__name__, instance_relative_config=True)
23+
APP.config.from_object("config.DefaultConfig")
24+
25+
# Create adapter.
26+
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
27+
SETTINGS = BotFrameworkAdapterSettings(APP.config["APP_ID"], APP.config["APP_PASSWORD"])
28+
ADAPTER = BotFrameworkAdapter(SETTINGS)
29+
30+
31+
# Catch-all for errors.
32+
async def on_error( # pylint: disable=unused-argument
33+
self, context: TurnContext, error: Exception
34+
):
35+
# This check writes out errors to console log .vs. app insights.
36+
# NOTE: In production environment, you should consider logging this to Azure
37+
# application insights.
38+
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
39+
40+
# Send a message to the user
41+
await context.send_activity("The bot encountered an error or bug.")
42+
await context.send_activity(
43+
"To continue to run this bot, please fix the bot source code."
44+
)
45+
# Send a trace activity if we're talking to the Bot Framework Emulator
46+
if context.activity.channel_id == "emulator":
47+
# Create a trace activity that contains the error object
48+
trace_activity = Activity(
49+
label="TurnError",
50+
name="on_turn_error Trace",
51+
timestamp=datetime.utcnow(),
52+
type=ActivityTypes.trace,
53+
value=f"{error}",
54+
value_type="https://www.botframework.com/schemas/error",
55+
)
56+
# Send a trace activity, which will be displayed in Bot Framework Emulator
57+
await context.send_activity(trace_activity)
58+
59+
60+
ADAPTER.on_turn_error = MethodType(on_error, ADAPTER)
61+
62+
MEMORY = MemoryStorage()
63+
ACTIVITY_LOG = ActivityLog(MEMORY)
64+
# Create the Bot
65+
BOT = MessageReactionBot(ACTIVITY_LOG)
66+
67+
# Listen for incoming requests on /api/messages.s
68+
@APP.route("/api/messages", methods=["POST"])
69+
def messages():
70+
# Main bot message handler.
71+
if "application/json" in request.headers["Content-Type"]:
72+
body = request.json
73+
else:
74+
return Response(status=415)
75+
76+
activity = Activity().deserialize(body)
77+
auth_header = (
78+
request.headers["Authorization"] if "Authorization" in request.headers else ""
79+
)
80+
81+
try:
82+
print("about to create task")
83+
task = LOOP.create_task(
84+
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
85+
)
86+
print("about to run until complete")
87+
LOOP.run_until_complete(task)
88+
print("is now complete")
89+
return Response(status=201)
90+
except Exception as exception:
91+
raise exception
92+
93+
94+
if __name__ == "__main__":
95+
try:
96+
APP.run(debug=False, port=APP.config["PORT"]) # nosec debug
97+
except Exception as exception:
98+
raise exception
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .message_reaction_bot import MessageReactionBot
5+
6+
__all__ = ["MessageReactionBot"]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from botbuilder.core import MessageFactory, TurnContext
5+
from botbuilder.core import ActivityHandler
6+
from botbuilder.schema import MessageReaction
7+
from typing import List
8+
from activity_log import ActivityLog
9+
10+
class MessageReactionBot(ActivityHandler):
11+
def __init__(self, activity_log: ActivityLog):
12+
self._log = activity_log
13+
14+
async def on_reactions_added(self, message_reactions: List[MessageReaction], turn_context: TurnContext):
15+
for reaction in message_reactions:
16+
activity = await self._log.find(turn_context.activity.reply_to_id)
17+
if not activity:
18+
await self._send_message_and_log_activity_id(turn_context, f"Activity {turn_context.activity.reply_to_id} not found in log")
19+
else:
20+
await self._send_message_and_log_activity_id(turn_context, f"You added '{reaction.type}' regarding '{activity.text}'")
21+
return
22+
23+
async def on_reactions_removed(self, message_reactions: List[MessageReaction], turn_context: TurnContext):
24+
for reaction in message_reactions:
25+
activity = await self._log.find(turn_context.activity.reply_to_id)
26+
if not activity:
27+
await self._send_message_and_log_activity_id(turn_context, f"Activity {turn_context.activity.reply_to_id} not found in log")
28+
else:
29+
await self._send_message_and_log_activity_id(turn_context, f"You removed '{reaction.type}' regarding '{activity.text}'")
30+
return
31+
32+
async def on_message_activity(self, turn_context: TurnContext):
33+
await self._send_message_and_log_activity_id(turn_context, f"echo: {turn_context.activity.text}")
34+
35+
async def _send_message_and_log_activity_id(self, turn_context: TurnContext, text: str):
36+
reply_activity = MessageFactory.text(text)
37+
resource_response = await turn_context.send_activity(reply_activity)
38+
39+
await self._log.append(resource_response.id, reply_activity)
40+
return
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License.
4+
5+
import os
6+
7+
8+
class DefaultConfig:
9+
""" Bot Configuration """
10+
11+
PORT = 3978
12+
APP_ID = os.environ.get("MicrosoftAppId", "")
13+
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
botbuilder-core>=4.4.0b1
2+
flask>=1.0.3
Loading
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
3+
"manifestVersion": "1.5",
4+
"version": "1.0.0",
5+
"id": "<<YOUR-MICROSOFT-BOT-APP-ID>>",
6+
"packageName": "com.teams.sample.conversationUpdate",
7+
"developer": {
8+
"name": "MessageReactions",
9+
"websiteUrl": "https://www.microsoft.com",
10+
"privacyUrl": "https://www.teams.com/privacy",
11+
"termsOfUseUrl": "https://www.teams.com/termsofuser"
12+
},
13+
"icons": {
14+
"color": "color.png",
15+
"outline": "outline.png"
16+
},
17+
"name": {
18+
"short": "MessageReactions",
19+
"full": "MessageReactions"
20+
},
21+
"description": {
22+
"short": "MessageReactions",
23+
"full": "MessageReactions"
24+
},
25+
"accentColor": "#FFFFFF",
26+
"bots": [
27+
{
28+
"botId": "<<YOUR-MICROSOFT-BOT-APP-ID>>",
29+
"scopes": [
30+
"groupchat",
31+
"team",
32+
"personal"
33+
],
34+
"supportsFiles": false,
35+
"isNotificationOnly": false
36+
}
37+
],
38+
"permissions": [
39+
"identity",
40+
"messageTeamMembers"
41+
],
42+
"validDomains": []
43+
}
Loading

0 commit comments

Comments
 (0)
0