8000 Merge pull request #424 from microsoft/trboehre-23.facebook-events · snifhex/botbuilder-python@57fff2e · GitHub
[go: up one dir, main page]

Skip to content

Commit 57fff2e

Browse files
authored
Merge pull request microsoft#424 from microsoft/trboehre-23.facebook-events
Added 23.facebook events
2 parents 4040105 + 5e275f0 commit 57fff2e

File tree

7 files changed

+520
-0
lines changed

7 files changed

+520
-0
lines changed

samples/23.facebook-events/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Facebook events
2+
3+
Bot Framework v4 facebook events bot sample
4+
5+
This bot has been created using [Bot Framework](https://dev.botframework.com), is shows how to integrate and consume Facebook specific payloads, such as postbacks, quick replies and optin events. Since Bot Framework supports multiple Facebook pages for a single bot, we also show how to know the page to which the message was sent, so developers can have custom behavior per page.
6+
7+
More information about configuring a bot for Facebook Messenger can be found here: [Connect a bot to Facebook](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-facebook)
8+
9+
## Running the sample
10+
- Clone the repository
11+
```bash
12+
git clone https://github.com/Microsoft/botbuilder-python.git
13+
```
14+
- Activate your desired virtual environment
15+
- Bring up a terminal, navigate to `botbuilder-python\samples\23.facebook-evbents` folder
16+
- In the terminal, type `pip install -r requirements.txt`
17+
- In the terminal, type `python app.py`
18+
19+
## Testing the bot using Bot Framework Emulator
20+
[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.
21+
22+
- Install the Bot Framework emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
23+
24+
### Connect to bot using Bot Framework Emulator
25+
- Launch Bot Framework Emulator
26+
- File -> Open Bot
27+
- Paste this URL in the emulator window - http://localhost:3978/api/messages
28+
29+
## Further reading
30+
31+
- [Bot Framework Documentation](https://docs.botframework.com)
32+
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
33+
- [Facebook Quick Replies](https://developers.facebook.com/docs/messenger-platform/send-messages/quick-replies/0)
34+
- [Facebook PostBack](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_postbacks/)
35+
- [Facebook Opt-in](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_optins/)
36+
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)

samples/23.facebook-events/app.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
8+
from flask import Flask, request, Response
9+
from botbuilder.core import (
10+
BotFrameworkAdapterSettings,
11+
TurnContext,
12+
BotFrameworkAdapter,
13+
)
14+
from botbuilder.schema import Activity, ActivityTypes
15+
16+
from bots import FacebookBot
17+
18+
# Create the loop and Flask app
19+
LOOP = asyncio.get_event_loop()
20+
app = Flask(__name__, instance_relative_config=True)
21+
app.config.from_object("config.DefaultConfig")
22+
23+
# Create adapter.
24+
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
25+
SETTINGS = BotFrameworkAdapterSettings(app.config["APP_ID"], app.config["APP_PASSWORD"])
26+
ADAPTER = BotFrameworkAdapter(SETTINGS)
27+
28+
29+
# Catch-all for errors.
30+
async def on_error(context: TurnContext, error: Exception):
31+
# This check writes out errors to console log .vs. app insights.
32+
# NOTE: In production environment, you should consider logging this to Azure
33+
# application insights.
34+
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
35+
36+
# Send a message to the user
37+
await context.send_activity("The bot encountered an error or bug.")
38+
await context.send_activity(
39+
"To continue to run this bot, please fix the bot source code."
40+
)
41+
# Send a trace activity if we're talking to the Bot Framework Emulator
42+
if context.activity.channel_id == "emulator":
43+
# Create a trace activity that contains the error object
44+
trace_activity = Activity(
45+
label="TurnError",
46+
name="on_turn_error Trace",
47+
timestamp=datetime.utcnow(),
48+
type=ActivityTypes.trace,
49+
value=f"{error}",
50+
value_type="https://www.botframework.com/schemas/error",
51+
)
52+
# Send a trace activity, which will be displayed in Bot Framework Emulator
53+
await context.send_activity(trace_activity)
54+
55+
56+
ADAPTER.on_turn_error = on_error
57+
58+
# Create the Bot
59+
BOT = FacebookBot()
60+
61+
# Listen for incoming requests on /api/messages
62+
@app.route("/api/messages", methods=["POST"])
63+
def messages():
64+
< F438 span class=pl-c># Main bot message handler.
65+
if "application/json" in request.headers["Content-Type"]:
66+
body = request.json
67+
else:
68+
return Response(status=415)
69+
70+
activity = Activity().deserialize(body)
71+
auth_header = (
72+
request.headers["Authorization"] if "Authorization" in request.headers else ""
73+
)
74+
75+
try:
76+
task = LOOP.create_task(
77+
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
78+
)
79+
LOOP.run_until_complete(task)
80+
return Response(status=201)
81+
except Exception as exception:
82+
raise exception
83+
84+
85+
if __name__ == "__main__":
86+
try:
87+
app.run(debug=False, port=app.config["PORT"]) # nosec debug
88+
except Exception as exception:
89+
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 .facebook_bot import FacebookBot
5+
6+
__all__ = ["FacebookBot"]
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from botbuilder.dialogs.choices import Choice, ChoiceFactory
5+
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext, CardFactory
6+
from botbuilder.schema import ChannelAccount, CardAction, ActionTypes, HeroCard
7+
8+
FACEBOOK_PAGEID_OPTION = "Facebook Id"
9+
QUICK_REPLIES_OPTION = "Quick Replies"
10+
POSTBACK_OPTION = "PostBack"
11+
12+
13+
class FacebookBot(ActivityHandler):
14+
async def on_members_added_activity(
15+
self, members_added: [ChannelAccount], turn_context: TurnContext
16+
):
17+
for member in members_added:
18+
if member.id != turn_context.activity.recipient.id:
19+
await turn_context.send_activity("Hello and welcome!")
20+
21+
async def on_message_activity(self, turn_context: TurnContext):
22+
if not await self._process_facebook_payload(
23+
turn_context, turn_context.activity.channel_data
24+
):
25+
await self._show_choices(turn_context)
26+
27+
async def on_event_activity(self, turn_context: TurnContext):
28+
await self._process_facebook_payload(turn_context, turn_context.activity.value)
29+
30+
async def _show_choices(self, turn_context: TurnContext):
31+
choices = [
32+
Choice(
33+
value=QUICK_REPLIES_OPTION,
34+
action=CardAction(
35+
title=QUICK_REPLIES_OPTION,
36+
type=ActionTypes.post_back,
37+
value=QUICK_REPLIES_OPTION,
38+
),
39+
),
40+
Choice(
41+
value=FACEBOOK_PAGEID_OPTION,
42+
action=CardAction(
43+
title=FACEBOOK_PAGEID_OPTION,
44+
type=ActionTypes.post_back,
45+
value=FACEBOOK_PAGEID_OPTION,
46+
),
47+
),
48+
Choice(
49+
value=POSTBACK_OPTION,
50+
action=CardAction(
51+
title=POSTBACK_OPTION,
52+
type=ActionTypes.post_back,
53+
value=POSTBACK_OPTION,
54+
),
55+
),
56+
]
57+
58+
message = ChoiceFactory.for_channel(
59+
turn_context.activity.channel_id,
60+
choices,
61+
"What Facebook feature would you like to try? Here are some quick replies to choose from!",
62+
)
63+
await turn_context.send_activity(message)
64+
65+
async def _process_facebook_payload(self, turn_context: TurnContext, data) -> bool:
66+
if "postback" in data:
67+
await self._on_facebook_postback(turn_context, data["postback"])
68+
return True
69+
70+
if "optin" in data:
71+
await self._on_facebook_optin(turn_context, data["optin"])
72+
return True
73+
74+
if "message" in data and "quick_reply" in data["message"]:
75+
await self._on_facebook_quick_reply(
76+
turn_context, data["message"]["quick_reply"]
77+
)
78+
return True
79+
80+
if "message" in data and data["message"]["is_echo"]:
81+
await self._on_facebook_echo(turn_context, data["message"])
82+
return True
83+
84+
async def _on_facebook_postback(
85+
self, turn_context: TurnContext, facebook_postback: dict
86+
):
87+
# TODO: Your PostBack handling logic here...
88+
89+
reply = MessageFactory.text(f"Postback: {facebook_postback}")
90+
await turn_context.send_activity(reply)
91+
await self._show_choices(turn_context)
92+
93+
async def _on_facebook_quick_reply(
94+
self, turn_context: TurnContext, facebook_quick_reply: dict
95+
):
96+
# TODO: Your quick reply event handling logic here...
97+
98+
if turn_context.activity.text == FACEBOOK_PAGEID_OPTION:
99+
reply = MessageFactory.text(
100+
f"This message comes from the following Facebook Page: {turn_context.activity.recipient.id}"
101+
)
102+
await turn_context.send_activity(reply)
103+
await self._show_choices(turn_context)
104+
elif turn_context.activity.text == POSTBACK_OPTION:
105+
card = HeroCard(
106+
text="Is 42 the answer to the ultimate question of Life, the Universe, and Everything?",
107+
buttons=[
108+
CardAction(title="Yes", type=ActionTypes.post_back, value="Yes"),
109+
CardAction(title="No", type=ActionTypes.post_back, value="No"),
110+
],
111+
)
112+
reply = MessageFactory.attachment(CardFactory.hero_card(card))
113+
await turn_context.send_activity(reply)
114+
else:
115+
print(facebook_quick_reply)
116+
await turn_context.send_activity("Quick Reply")
117+
await self._show_choices(turn_context)
118+
119+
async def _on_facebook_optin(self, turn_context: TurnContext, facebook_optin: dict):
120+
# TODO: Your optin event handling logic here...
121+
print(facebook_optin)
122+
await turn_context.send_activity("Opt In")
123+
124+
async def _on_facebook_echo(
125+
self, turn_context: TurnContext, facebook_message: dict
126+
):
127+
# TODO: Your echo event handling logic here...
128+
print(facebook_message)
129+
await turn_context.send_activity("Echo")

samples/23.facebook-events/config.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
""" Bot Configuration """
8+
9+
10+
class DefaultConfig:
11+
""" Bot Configuration """
12+
13+
PORT = 3978
14+
APP_ID = os.environ.get("MicrosoftAppId", "")
15+
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")

0 commit comments

Comments
 (0)
0