Search code examples
pythonjsonwebhooksapi-ai

Python Webhook: Passing through a URL + payload


I'm a beginner with Python and trying to build a service that takes information from api.ai, passes it to an API, then returns a confirmation message from the JSON it returns.

app.py:

#!/usr/bin/env python

from __future__ import print_function
from future.standard_library import install_aliases
install_aliases()

from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError

import json
import os
import sys
import logging

from flask import Flask, render_template
from flask import request
from flask import make_response

# Flask app should start in global layout
app = Flask(__name__)
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)

@app.route('/webhook', methods=['POST'])

def webhook():
    req = request.get_json(silent=True, force=True)

    print("Request:")
    print(json.dumps(req, indent=4))

    res = processRequest(req)

    res = json.dumps(res, indent=4)
    # print(res)
    r = make_response(res)
    r.headers['Content-Type'] = 'application/json'
    return r

def processRequest(req):
    if req.get("result").get("action") != "bookMyConference":
        return {}

    #oauth
    orequest = req.get("originalRequest") # work down the tree
    odata = orequest.get("data") # work down the tree
    user = odata.get("user") # work down the tree
    access_token = user.get("access_token")

    #data
    result = req.get("result") # work down the tree
    parameters = result.get("parameters") # work down the tree
    startdate = parameters.get("start-date")
    meetingname = parameters.get("meeting-name")

    payload = {
        "start-date": startdate,
        "end-date": startdate,
        "meeting-name": meetingname
    }

    # POST info to join.me
    baseurl = "https://api.join.me/v1/meetings"
    p = Request(baseurl)
    p.add_header('Content-Type', 'application/json; charset=utf-8')
    p.add_header('Authorization', 'Bearer ' + access_token) #from oauth
    jsondata = json.dumps(payload)
    jsondataasbytes = jsondata.encode('utf-8')   # needs to be bytes
    jresult = urlopen(p, jsondataasbytes).read()
    data = json.loads(jresult)
    res = makeWebhookResult(data)
    return res

def makeWebhookResult(data):

    speech = "Appointment scheduled!"

    print("Response:")
    print(speech)

    return {
        "speech": speech,
        "displayText": speech,
        # "data": data,
        "source": "heroku-bookmyconference"
    }


if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))

    print("Starting app on port %d" % port)

    app.run(debug=False, port=port, host='0.0.0.0')

Edit 4: Here's the error I'm getting in my Heroku logs:

2017-03-21T19:06:09.383612+00:00 app[web.1]: HTTPError: HTTP Error 400: Bad Request


Solution

  • Borrowing from here, using urlib modules inside processRequest() you could add your payload to urlopen like this:

    req = Request(yql_url)
    req.add_header('Content-Type', 'application/json; charset=utf-8')
    jsondata = json.dumps(payload)
    jsondataasbytes = jsondata.encode('utf-8')   # needs to be bytes
    result = urlopen(req, jsondataasbytes).read()
    data = json.loads(result)
    

    Things get more succinct if using the requests module:

    headers = {'content-type': 'application/json'}
    result = requests.post(yql_url, data=json.dumps(payload), headers=headers)
    data = result.json()
    

    EDIT: Adding some details specific to the join.me api

    Looking at the join.me docs you'll need to obtain an access token to add to your header. But you also need an app auth code before you can get an access token. You can get the app auth code manually, or by chaining some redirects.

    To get started, try this url in your browser and get the code from the callback params. Using your join.me creds:

    auth_url = 'https://secure.join.me/api/public/v1/auth/oauth2' \
        + '?client_id=' + client_id \
        + '&scope=scheduler%20start_meeting' \
        + '&redirect_uri=' + callback_url \
        + '&state=ABCD' \
        + '&response_type=code'
    print(auth_url)  # try in browser
    

    To get an access token:

    token_url = 'https://secure.join.me/api/public/v1/auth/token'
    headers = {'content-type': 'application/json'}
    token_params = {
        'client_id': client_id,
        'client_secret': client_secret,
        'code': auth_code,
        'redirect_uri': callback_url,
        'grant_type': 'authorization_code'
    }
    result = requests.post(token_url, data=json.dumps(token_params), headers=headers)
    access_token = result.json().get('access_token')
    

    Then your header for the post to /meetings would need to look like:

    headers = {
        'content-type': 'application/json',
        'Authorization': 'Bearer ' + access_token
    }