FB Messenger can't receive messages - python

I am trying to setup a messenger chatbot using the newly released messenger platform api. I setup a Python Flask server hosted on Heroku and have been adapting these instruction to try to get my page receiving the messages my server sends it: https://developers.facebook.com/docs/messenger-platform/quickstart
Thus far I have validated a callback url and have been able to receive messages when I post to my page on FB (i.e. when I message my page which is linked to my app on FB, my heroku logs show that the POST request is being received). However, when I try to send messages from my server to my app, I get the following JSON error response:
400: {"error":{"message":"(#100) param recipient must be non-empty.","type":"OAuthException","code":100,"fbtrace_id":"B3cni+LAmYU"}}
I am using the requests library to send requests to the page. Below is the code I am using to service POST requests:
import json
import os
import requests
from flask import Flask, request
app = Flask(__name__)
FB_MESSAGES_ENDPOINT = "https://graph.facebook.com/v2.6/me/messages"
FB_TOKEN = "EAADKWAcVj...AZDZD"
#app.route('/', methods=["POST"])
def chatbot_response():
req_data = request.data
data = json.loads(req_data)
print "Data: ", data
sender_id = data["entry"][0]["messaging"][0]["sender"]["id"]
print "Sender id: ", sender_id
send_back_to_fb = {
"entry": [{"messaging": [{"recipient": {"id": str(sender_id)}}]}],
"recipient": {
"id": str(sender_id)},
"message": "this is a test response message",
"recipient": str(sender_id), "access_token": FB_TOKEN
}
params_input = {"access_token": FB_TOKEN, "recipient": sender_id}
fb_response = requests.post(FB_MESSAGES_ENDPOINT,
params={"access_token": FB_TOKEN, "recipient": {"id": str(sender_id)}, "json": "recipient": {"id": str(sender_id)}},
data=json.dumps(send_back_to_fb))
print "Json of response: ", fb_response.json()
# handle the response to the subrequest you made
if not fb_response.ok:
# log some useful info for yourself, for debugging
print 'jeepers. %s: %s' % (fb_response.status_code, fb_response.text)
return "OK", 200
if __name__ == '__main__':
app.run(host="0.0.0.0")
I've tried countless different types of key/value encodings of the 'recipients' element into json but none of them seem to be understood by the FB graph service. How can I encode my request so that FB knows what the 'recipient' param is?
Thanks!
Edit:
It turns out I had to manually set the encoding type in the header of the POST request. Adding the following line made it so I could send interpretable text responses to FB:
headers = {'content-type': 'application/json'}

You can try these, both should work
headers = {'Content-type': 'application/json'}
response = requests.post(url, data=json.dumps(send_back_to_fb), headers=headers)
OR
response = requests.post(url, json=send_back_to_fb)

how about python library for using facebook messenger platform?
https://github.com/conbus/fbmq
from flask import Flask, request
from fbmq import Page
page = fbmq.Page(PAGE_ACCESS_TOKEN)
#app.route('/webhook', methods=['POST'])
def webhook():
page.handle_webhook(request.get_data(as_text=True))
return "ok"
#page.handle_message
def message_handler(event):
page.send(event.sender_id, "HI!!!")

Related

What do I need to change so my tornado code can post successfully?

I have the following (with some strings modified) which works when using the requests library.
import requests
from pprint import pprint
import json
PEM = '/full/path/to/my.pem'
client_id='cliendID'
client_secret='clientSecret'
USER='myuser'
PSWD='mypwd'
url = 'https://theurl.com/request/'
data = {
"grant_type": "password",
"username": USER,
"password": PSWD
}
auth = (client_id, client_secret)
response = requests.post( url, auth=auth, data=data, verify=PEM )
answer = response.json()['answer']
print( answer )
The answer printed is what I expect.
The following usage of curl also works:
curl -iv --user cliendID:clientSecret --key /full/path/to/server.key --cert /full/path/to/my.pem -F grant_type=password -F username=myuser -F password=mypwd https://theurl.com/request/
However, when I try to do the same using Tornado and AsyncHTTPClient, I get a "Bad Request" response. Some sample Tornado code is:
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
import json
async def get_content():
PEM = '/full/path/to/my.pem'
client_id='cliendID'
client_secret='clientSecret'
url = 'https://theurl.com/request/'
USER='myuser'
PSWD='mypwd'
data = {
"grant_type": "password",
"username": USER,
"password": PSWD
}
bodyData = json.dumps( data )
http_client = AsyncHTTPClient()
response = await http_client.fetch( url,
method = 'POST',
body = bodyData,
auth_username = client_id,
auth_password = client_secret,
ca_certs = PEM )
print( response.body.decode() )
async def main():
await get_content()
if __name__ == "__main__":
io_loop = IOLoop.current()
io_loop.run_sync(main)
If I had to guess, I believe the issue is with how I am sending the bodyData.
What do I need to change in the Tornado code so this will work...?
By default, the requests library and Tornado's AsyncHTTPClient, both, send the body data as form encoded data (i.e. with Content-Type: application/x-www-form-urlencoded).
The requests library automatically encodes the data dict with the correct content type.
But with Tornado's client, you're requried to manually encode the data as follows:
from urllib.parse import urlencode
bodyData = urlencode(data)
...
Cause of the error:
You're getting the Bad Request error because you're sending the data as JSON but the Content-Type header (sent automatically by Tornado) still says www-form-urlencoded. That means the server can't decode your supplied data because it doesn't know that it's JSON.

Get data from an API with Flask

I have a simple flask app, which is intended to make a request to an api and return data. Unfortunately, I can't share the details, so you can reproduce the error. The app looks like that:
from flask import Flask
import requests
import json
from requests.auth import HTTPBasicAuth
app = Flask(__name__)
#app.route("/")
def getData():
# define variables
username = "<username>"
password = "<password>"
headers = {"Authorization": "Basic"}
reqHeaders = {"Content-Type": "application/json"}
payload = json.dumps(
{
"jobType": "<jobType>",
"jobName": "<jobName>",
"startPeriod": "<startPeriod>",
"endPeriod": "<endPeriod>",
"importMode": "<importMode>",
"exportMode": "<exportMode>"
}
)
jobId = 7044
req = requests.get("<url>", auth=HTTPBasicAuth(username, password), headers=reqHeaders, data=payload)
return req.content
if __name__ == "__main__":
app.run()
However, when executed this returns error 500: Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
The same script but outside a flask app (just the function as it is defined here) runs with no problems at all.
What am I doing wrong?
flask app return format json. If you return req.content, it will break function. You must parse response request to json before return it.
from flask import jsonify
return jsonify(req.json())
It's better with safe load response when the request fail
req = requests.get()
if req.status_code !=200:
return {}
else:
return jsonify(req.json())

How can i build a proper restful response API by python flask for parameters

I'm studing about RESFful API with python.
I want to build a my restful api server,
but i have a problem,
i don't know how my api server returns proper data by reqeust each parameters
(request code sample)
the request code wants to get the information about 'item' : 'sword'
import requests
import json
url = "https://theURL"
querystring={"item":"sword"}
response = requests.request("GET", url, params=querystring)
print (response.json())
(API server response code sample, by flask python)
from flask import Flask, url_for
from flask_restful import Resource, Api, abort, reqparse
app = Flask(__name__)
api = Api(app)
TODOS = {
"version":"2.0",
"resultCode":"OK",
"output":{
{
"item" :"sword"
"price": 300,
"damage": 20,
},
{
"item" :"gun"
"price": 500,
"damage": 30,
},
},
}
class Todo(Resource):
def post(self):
return TODOS
api.add_resource(Todo, '/item.price')
if __name__ == "__main__":
app.run(debug=True, host ="192.168.0.8", port = 8080)
So i want to know how i use a code in response api server for returning 'item price' data by reqeusted parameters 'item : sword'
I just want to get the selected parameter's item price and damage information.
I thought it might be very easy, i'm tried to search the example but i couldn't find proper sample code.
I'm not a Flask-expert, but this helps setting up and running a minimalistic Flask-server. Then, this explains how to return json-formatted data from your server, and finally how to request and interpret the json-response can be found here. Summarized below:
Server returning a json-formatted data:
from flask import Flask
from flask import jsonify
app = Flask(__name__)
#app.route('/')
#app.route('/index')
def hello():
return "Hello, World!"
#app.route('/request_sword')
def grant_sword():
return jsonify({"sword": {"name":"Excalibur","price":"stack overflow"}})
Client requesting json-formatted data:
import json
import urllib.request
url = "http://127.0.0.1:5000/request_sword" ## My local host flask server
data = urllib.request.urlopen(url)
response = json.loads(data.read())
print(response)
That's all really. You may also just enter the url in your browser, which will correctly read the json-data:

Making an Angular2 POST with JSON

I have a simple Flask backend for the API, and want to make a POST.
I'm not sure the error is-I get the error message, but am unsure if its an Angular or Flask issue. I tried request.get_json() in my Flask, and got
{BadRequest}400 BadRequest: The browser (or proxy) sent a request that
this server could not understand
Angular2 call in my service.ts:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable} from 'rxjs/Rx';
import { Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class NicksService {
private headers = new Headers({'Content-Type': 'application/json'});
nickUrl: string = "http://localhost:5000/nick";
constructor(private http: Http) { }
// Send the nick to the server to see if it is valid
checkNickValidity(nickName: string): Observable<string>{
return this.http.post(this.nickUrl, JSON.stringify({nick: nickName}), {headers: this.headers})
.map(this.extractData)
.catch(this.handleError)
}
Flask backend:
from flask_cors import CORS
from flask.ext.api import FlaskAPI
from flask import request
app = FlaskAPI(__name__)
CORS(app)
#app.route('/')
#app.route('/nick', methods=['POST'])
def check_nick():
try:
nick = request.json
if nick =='AmandaPanda':
return 'Good'
else:
return 'No nick found'
except:
return 'error'
if __name__ == '__main__':
app.run()
I think you have problems on both sides of your application.
Client side:
Angular's Http's post method receives any object in for the body parameter, meaning you don't need to stringify it.
E.g.:
this.http.post(this.nickUrl, {nick: nickName}, {headers: this.headers})
Server side:
Once the client's request arrived properly to the server, you are not getting the data properly.
What you need to do is something like this:
from flask import jsonify
(... your code ...)
data = request.json
nick = data.get('nick')
if nick == 'AmandaPanda':
return jsonify({'message': 'good'}), 200
else:
return jsonify({'message': 'no nick found'}), 400
Here, the json data is stored as a dictionary in the variable data. Later, the nick is obtained from the nick key of said dictionary.
Finally, it is a better practice to return to the client in json form. That's what Flask's jsonify method is for. Notice the 200 and 400 status codes appended to the response, to indicate to the client if the request was properly accepted (200) or if it was a bad request (400).

MailChimp bad request JSONParseError with Python

I am trying to hook up to MailChimp's api, in my Django application, to add an email to one of my lists. Seems pretty simple enough. I add my api key to the header of the request, with the email address and other variable in the body of the request. every time I try and connect though, I get a response status code of 400. The message says there is a JSON parsing error, and that my JSON is either formatted incorrectly, or there is missing data required for the request. I am making this same api call however with Postman, and am getting a good response back.
view function
import requests
def join_newsletter(request, email):
# hash the user's email for mailchimp's API
# m = hashlib.md5()
# c_email = email
# m.update(c_email.encode('utf-8'))
# email_hash = m.hexdigest()
api_key = 'apikey ' + settings.MAILCHIMP_API
api_endpoint = api_endpoint
data = {
"email_address": email,
"status": "subscribed"
}
header = {
'Authorization': api_key
}
r = requests.post(api_endpoint, data=data, headers=header)
message = r.content
return message

Categories