write fb bot in python3
TRANSCRIPT
Write a Facebook messenger bot in Python3
Jim
Outline
Some basic knowledges and some requirements
Setup server with FB
Write an echo bot
Further configuration
Communicate with FB
FB Server
To react with FB, we need a web api server which accept
FB’s message and response to it.
Response to a message
FB Server
Response to a message
FB Server
Response to a message
FB Server My Server
Recall
How we get a web page?
We type an url address (ip address) onto the location bar
Chrome sends a request to the server
Server responses us with corresponded return
Web server
To finish a web request, you need
An IP address
A Server
Accept HTTP Protocol
HTTP
A way to communicate with server
A general way to exchange information
Ask for resources from server (request)
Server returns if a request is valid (response)
Request and Response
My ServerGET /index.html
HTML Content
Try with command line
$ telnet www.google.com 80
GET / HTTP/1.1
(enter)
Methods
GET (Usually used for get web page)
POST (Usually used for submit a form)
Others (PUT, DELETE, OPTION) (Won’t mention today)
Parameters
Query string (on the url link)
Form submit body
Raw body <— We will use today
Headers
HTTPS
What is HTTPS? HTTP with Secure
Why HTTPS
Make you info secure
Create a security tunnel before data transfer
How to setup it? It’s complicated…
API
Application Programming Interface
A way to offer resources
Could be a server or a library
Sample codes
https://github.com/lemonlatte/myCurrencyBot
Write you own API server
Flask
Flask is a simple but flexible and powerful web framework
Strong community support
Setup a local environment
$ virtualenv -p python3 .venv
$ . .venv/bin/activate
exit
Before this, we need to setup your python environment
$ pip3 install flask
First web server
#!/usr/bin/env python
from flask import Flaskfrom flask import abortfrom flask import requestapp = Flask(__name__)
@app.route("/version", methods=['GET'])def version(): if request.method == 'GET': return "0.1" else: abort(404)
if __name__ == "__main__": app.run(port=11123)
Try telnet to your web server
$ telnet localhost 11123Trying ::1...Trying 127.0.0.1...Connected to localhost.Escape character is '^]'.GET /version HTTP/1.1
HTTP/1.0 200 OKContent-Type: text/html; charset=utf-8Content-Length: 3Server: Werkzeug/0.12.1 Python/3.5.2Date: Sun, 16 Apr 2017 15:54:35 GMT
0.1Connection closed by foreign host.(enter)
Start building a bot
Now, we need a web server
FB Server My Server
What we have to do?
Set up you account on Facebook
Write a web server
1. Go https://developers.facebook.com
2. My Apps —> Add a New App
3. Add Product —> Messenger —> Get Started
4. Setup Webhooks, you need
a. A callback url (require HTTPS)
b. A random string as the token
5. Subscription Fields: messages, messaging_postbacks
How to setup a bot (I)
Setup Webhooks
BOT_TOKEN = "your-random-string"
@app.route("/fbCallback", methods=['GET', 'POST'])def fb_cb_handler(): if request.method == 'GET': token = request.args.get('hub.verify_token') if token == BOT_TOKEN: return request.args.get('hub.challenge') else: abort(403) else: abort(405)
Echo Bot
A web call accept requests from FB
Parse data from FB
Return a text response to FB
How to setup a bot (II)
1. Create a fan page
2. Create a new App in “facebook developers” page
a. Category : Brand or Product / App Page
3. Back to developer page:
a. Generate page access token
b. Subscribe messages from your page
Get messages
def fb_post_handler(req): print(req.get_data()) resp_body = req.get_json() return ""
@app.route("/fbCallback", methods=['GET', 'POST'])def fb_cb_handler(): if request.method == 'GET': . . . elif request.method == 'POST': return fb_post_handler(request) else: . . .
Test - talk to your bot
Payload{ "object": "page", "entry": [ { "id": "708403975999401", "time": 1492008179540, "messaging": [ { "sender": { "id": "1433303610077218" }, "recipient": { "id": "708403975999401" }, "timestamp": 1492008179350, "message": { "mid": "mid.$cAAKEShkF7FJhk-NellbYp4VzAIdE", "seq": 105605, "text": "hahaha" } } ] } ]}
Send messages
Install an additional requirement:
$ pip3 install requests
Send text function
import requests
PAGE_TOKEN = "your-page-access-token"FB_MESSENGER_URI = "https://graph.facebook.com/v2.6/me/messages?access_token=" + PAGE_TOKEN
def send_text(reply_token, text): data = { "recipient": {"id": reply_token}, "message": {"text": text} } r = requests.post(FB_MESSENGER_URI, json=data) if r.status_code != requests.codes.ok: print(r.content)
Return messages to a user
def fb_post_handler(req): print req.get_data() resp_body = req.get_json()
for entry in resp_body["entry"]: for msg in entry["messaging"]: sender = msg['sender']['id'] if 'message' in msg: if msg['message'].get('is_echo'): return "" if 'text' not in msg[‘message']: return "" text = msg['message']['text'] send_text(sender, text) return ""
Test - talk to you bot again
Messenger Profile
Facebook provides some API call for us to configure the BOT environment
•Greeting - A help message to show a user what the purpose of the bot is
•Get Started - Setup the postback message when a user is first login
•Persistent Menu
Messenger Profile API
POST
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<your-page-access-token>
A JSON body for the configurations
Greeting
curl -X POST \ 'https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<your-page-access-token>' \ -H 'content-type: application/json' \ -d '{ "greeting":[ { "locale":"default", "text":"嗨,我會幫你查匯率"
} ] }'
Get Started
curl -X POST \ 'https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<your-page-access-token>' \ -H 'content-type: application/json' \ -d '{ "get_started":{ "payload":"GET_STARTED" }}'
POSTBACK
Some user-defined words which represents some kinds of events have been triggered.
For example, once a new user starts chatting with the bot, a predefined postback message will sent to your server.
Payload{ "object": "page", "entry": [ { "id": "708403975999401", "time": 1492364568758, "messaging": [ { "recipient": { "id": "708403975999401" }, "timestamp": 1492364568758, "sender": { "id": "1433303610077218" }, "postback": { "payload": "GET_STARTED" } } ] } ]}
Handle the POSTBACK
def fb_post_handler(req): print req.get_data() resp_body = req.get_json()
for entry in resp_body["entry"]: for msg in entry["messaging"]: sender = msg['sender']['id'] if 'message' in msg: if msg['message'].get('is_echo'): return "" text = msg['message']['text'] send_text(sender, text) elif 'postback' in msg: if msg['postback']['payload'] == "GET_STARTED": send_text(sender, 'welcome') return ""
Send More
Generic Template
Generic template is some FB predefined rich message format.
Lists, Images, Buttons
Generic Template{ "message": { "attachment": { "type": "template", "payload": { "template_type": "generic", "elements": [ { "title": "BTC - USD", "image_url": "<image url>", "subtitle": "The currency between BTC and USD" } ] } } }}
Image
{ "title": "BTC - USD", "image_url": "<image url>", "subtitle": "The currency between BTC and USD"}
Send Template Functiondef send_template_message(user_id, elements): data = { "recipient":{ "id": user_id }, "message":{ "attachment": { "type":"template", "payload":{ "template_type":"generic", "elements": elements } } } }
r = requests.post(FB_MESSENGER_URI, json=data) if r.status_code != requests.codes.ok: print(r.content)
def fb_post_handler(req): . . . for entry in resp_body["entry"]: for msg in entry["messaging"]: sender = msg['sender']['id'] if 'message' in msg: . . . text = msg['message']['text'] if text == "btcusd": element = [{ "title":"<title>", "image_url":"<url>", "subtitle":"<sub title>" }] send_template_message(sender, element) else: send_text(sender, text) elif 'postback' in msg: . . .
Button Types
web_url - an url link
postback - some customized events
Etc.
Image with URL Button{ "title": "BTC - USD", "image_url": "<image url>", "subtitle": "The currency between BTC and USD", "buttons": [ { "type": "web_url", "url": "https://btc-e.com/exchange/btc_usd", "title": "View in BTC-E“ } ]}
if 'message' in msg: . . . text = msg['message']['text'] if text == "btcusd": element = [{ "title":"<title>", "image_url":"<url>", "subtitle":"<sub title>", "buttons": [ {"type": "web_url", "url": "https://btc-e.com/exchange/btc_usd", "title": "View in BTC-E" } ] }] send_template_message(sender, element) else: send_text(sender, text) elif 'postback' in msg: . . .
Image with POSTBACK Button
{ "title": "BTC - USD", "image_url": "<image url>", "subtitle": "The currency between BTC and USD", "buttons": [ { "type": "postback", "payload": "HAHAHA", "title": "Laugh" } ]}
def fb_post_handler(req): . . . element = [{ . . . "buttons": [ {"type": "web_url", "url": "", "title": "View in BTC-E" }, {"type": "postback", "payload": "HAHAHA", "title": "Laugh" } ] }] . . . if msg['postback']['payload'] == "GET_STARTED": send_text(sender, 'welcome') elif msg['postback']['payload'] == "HAHAHA": send_text(sender, 'hahaha!') . . .
Ask a question
FB can also send some quick answers for a user to select
This is called: quick-replies
Quick Replies
"message":{ "text":"Query currency?”, "quick_replies":[ { "content_type":"text", "title":"Yes", "payload":"YES" }, { "content_type":"text", "title":"No", "payload":"NO", } ]}
Extend our send_text function
def send_text(reply_token, text, answers): data = { "recipient": {"id": reply_token}, "message": {"text": text} } if answers: data["message"]["quick_replies"] = answers r = requests.post(FB_MESSENGER_URI, json=data) if r.status_code != requests.codes.ok: print(r.content)
def fb_post_handler(req): . . .
send_text(sender, text, None) . . .
Send quick replies
. . .elif text == "btcusd": . . .
elif text == "Btc": send_text(sender, "Query currency?", [ {"content_type":"text", "title":"Yes", "payload":"QUERY_CURRENCY" }, {"content_type":"text", "title":"No", "payload":"CANCEL" } ]) . . .
Payload{ "object": "page", "entry": [ { "id": "708403975999401", "time": 1492411242498, "messaging": [ { "sender": { "id": "1433303610077218" }, "recipient": { "id": "708403975999401" }, "timestamp": 1492411242366, "message": { "quick_reply": { "payload": "CANCEL" }, "mid": "mid.$cAAKEShkF7FJhq-mfflbeqRWhUtKd", "seq": 106589, "text": "No" } } ] } ]}
Handle quick replies response
. . .
if 'message' in msg: if msg['message'].get('is_echo'): return "" if 'text' not in msg['message']: return "" if 'quick_reply' in msg['message']: reply = msg["message"]["quick_reply"] if reply['payload'] == "QUERY_CURRENCY": send_text(sender, "This function is not worked yet.", None) elif reply['payload'] == "CANCEL": send_text(sender, "No problem.", None) return "" text = msg['message']['text'] if text == “btcusd":
. . .
Review
Go back to developer page
Submit a review
Make a video
Not mentioned
Location
Persistent menu
Lots of advanced features
Reference
http://flask.pocoo.org/
https://developers.facebook.com/docs/messenger-platform
http://docs.python-requests.org/en/master/
Q & A