Skip to content
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
36 changes: 36 additions & 0 deletions samples/23.facebook-events/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Facebook events

Bot Framework v4 facebook events bot sample

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.

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)

## 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\23.facebook-evbents` 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
- File -> Open Bot
- 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)
- [Facebook Quick Replies](https://developers.facebook.com/docs/messenger-platform/send-messages/quick-replies/0)
- [Facebook PostBack](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_postbacks/)
- [Facebook Opt-in](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_optins/)
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
89 changes: 89 additions & 0 deletions samples/23.facebook-events/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import asyncio
import sys
from datetime import datetime

from flask import Flask, request, Response
from botbuilder.core import (
BotFrameworkAdapterSettings,
TurnContext,
BotFrameworkAdapter,
)
from botbuilder.schema import Activity, ActivityTypes

from bots import FacebookBot

# Create the loop and Flask app
LOOP = asyncio.get_event_loop()
app = Flask(__name__, instance_relative_config=True)
app.config.from_object("config.DefaultConfig")

# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = BotFrameworkAdapterSettings(app.config["APP_ID"], app.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 = FacebookBot()

# Listen for incoming requests on /api/messages
@app.route("/api/messages", methods=["POST"])
def messages():
# Main bot message handler.
if "application/json" in request.headers["Content-Type"]:
body = request.json
else:
return Response(status=415)

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

try:
task = LOOP.create_task(
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
)
LOOP.run_until_complete(task)
return Response(status=201)
except Exception as exception:
raise exception


if __name__ == "__main__":
try:
app.run(debug=False, port=app.config["PORT"]) # nosec debug
except Exception as exception:
raise exception
6 changes: 6 additions & 0 deletions samples/23.facebook-events/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 .facebook_bot import FacebookBot

__all__ = ["FacebookBot"]
129 changes: 129 additions & 0 deletions samples/23.facebook-events/bots/facebook_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from botbuilder.dialogs.choices import Choice, ChoiceFactory
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext, CardFactory
from botbuilder.schema import ChannelAccount, CardAction, ActionTypes, HeroCard

FACEBOOK_PAGEID_OPTION = "Facebook Id"
QUICK_REPLIES_OPTION = "Quick Replies"
POSTBACK_OPTION = "PostBack"


class FacebookBot(ActivityHandler):
async def on_members_added_activity(
self, members_added: [ChannelAccount], turn_context: TurnContext
):
for member in members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity("Hello and welcome!")

async def on_message_activity(self, turn_context: TurnContext):
if not await self._process_facebook_payload(
turn_context, turn_context.activity.channel_data
):
await self._show_choices(turn_context)

async def on_event_activity(self, turn_context: TurnContext):
await self._process_facebook_payload(turn_context, turn_context.activity.value)

async def _show_choices(self, turn_context: TurnContext):
choices = [
Choice(
value=QUICK_REPLIES_OPTION,
action=CardAction(
title=QUICK_REPLIES_OPTION,
type=ActionTypes.post_back,
value=QUICK_REPLIES_OPTION,
),
),
Choice(
value=FACEBOOK_PAGEID_OPTION,
action=CardAction(
title=FACEBOOK_PAGEID_OPTION,
type=ActionTypes.post_back,
value=FACEBOOK_PAGEID_OPTION,
),
),
Choice(
value=POSTBACK_OPTION,
action=CardAction(
title=POSTBACK_OPTION,
type=ActionTypes.post_back,
value=POSTBACK_OPTION,
),
),
]

message = ChoiceFactory.for_channel(
turn_context.activity.channel_id,
choices,
"What Facebook feature would you like to try? Here are some quick replies to choose from!",
)
await turn_context.send_activity(message)

async def _process_facebook_payload(self, turn_context: TurnContext, data) -> bool:
if "postback" in data:
await self._on_facebook_postback(turn_context, data["postback"])
return True

if "optin" in data:
await self._on_facebook_optin(turn_context, data["optin"])
return True

if "message" in data and "quick_reply" in data["message"]:
await self._on_facebook_quick_reply(
turn_context, data["message"]["quick_reply"]
)
return True

if "message" in data and data["message"]["is_echo"]:
await self._on_facebook_echo(turn_context, data["message"])
return True

async def _on_facebook_postback(
self, turn_context: TurnContext, facebook_postback: dict
):
# TODO: Your PostBack handling logic here...

reply = MessageFactory.text(f"Postback: {facebook_postback}")
await turn_context.send_activity(reply)
await self._show_choices(turn_context)

async def _on_facebook_quick_reply(
self, turn_context: TurnContext, facebook_quick_reply: dict
):
# TODO: Your quick reply event handling logic here...

if turn_context.activity.text == FACEBOOK_PAGEID_OPTION:
reply = MessageFactory.text(
f"This message comes from the following Facebook Page: {turn_context.activity.recipient.id}"
)
await turn_context.send_activity(reply)
await self._show_choices(turn_context)
elif turn_context.activity.text == POSTBACK_OPTION:
card = HeroCard(
text="Is 42 the answer to the ultimate question of Life, the Universe, and Everything?",
buttons=[
CardAction(title="Yes", type=ActionTypes.post_back, value="Yes"),
CardAction(title="No", type=ActionTypes.post_back, value="No"),
],
)
reply = MessageFactory.attachment(CardFactory.hero_card(card))
await turn_context.send_activity(reply)
else:
print(facebook_quick_reply)
await turn_context.send_activity("Quick Reply")
await self._show_choices(turn_context)

async def _on_facebook_optin(self, turn_context: TurnContext, facebook_optin: dict):
# TODO: Your optin event handling logic here...
print(facebook_optin)
await turn_context.send_activity("Opt In")

async def _on_facebook_echo(
self, turn_context: TurnContext, facebook_message: dict
):
# TODO: Your echo event handling logic here...
print(facebook_message)
await turn_context.send_activity("Echo")
15 changes: 15 additions & 0 deletions samples/23.facebook-events/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import os

""" Bot Configuration """


class DefaultConfig:
""" Bot Configuration """

PORT = 3978
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
Loading