Always receiving 400:Bad request when Twilio webhook is configured - python

I want to maintain seperate twilio phone unsubscribers list and this can be done when webhook is configured via twilio console to receive incoming messages. I would parse only those messages when some one types "STOP". I have successfully configured webhook
Now from my phone when i type "STOP" to my twilio number, I am receiving always bad request. My code looks as follows
#app.route('/twilio/unsubscribes_incremental', methods=['POST', 'GET'])
def phone_unsubscribes_incremental():
print("start")
print("The arguments are ", request.args)
payload = request.get_json(force=True)
print("The payload is ", payload)
#resp = MessagingResponse()
if payload.get('Body') in twilio_unsubscribe_list:
stream_data_to_bq(payload)
#resp.message("")
#return str(resp)
return jsonify({"status":"ok"})
My python console shows as follows
My ngrok console shows as follows
My twilio console logs shows as follows
For some reason iam unable to parse request object sent to my webhook. This account is in free trial. Can any one point out to me right documentation to parse incoming messages.

Twilio developer evangelist here.
As Alan points out, Twilio webhook requests send data in the form application/x-www-form-urlencoded, the same format that a web form will post data. It is not JSON. Twilio also expects your application's response to be application/xml.
So, you should read the data from request.form and, since it doesn't look like you are expecting to tell Twilio to do anything further with the request, return an empty <Response> TwiML element.
Something like this should work:
#app.route('/twilio/unsubscribes_incremental', methods=['POST', 'GET'])
def phone_unsubscribes_incremental():
print("start")
print("The arguments are ", request.args)
payload = request.form
print("The payload is ", payload)
if payload.get('Body') in twilio_unsubscribe_list:
stream_data_to_bq(payload)
resp = MessagingResponse()
return str(resp), { 'Content-Type': 'application/xml' }

The webhook is application/x-www-form-urlencoded
Webhook FAQ
For inbound text messages, Twilio will send an HTTP POST request to
your server with a body that uses the
application/x-www-form-urlencoded encoding. View the list of
parameters sent in that request.

Related

Twilio whatapp bot not responding to keywords

I am trying to build a whatsapp bot on Twilio following the Twilio tutorial.
I have written the flask program to create a test bot:
from flask import Flask, request
import requests
from twilio.twiml.messaging_response import MessagingResponse
app = Flask(__name__)
#app.route('/bot', methods=['POST'])
def bot():
incoming_msg = request.values.get('Body', '').lower()
resp = MessagingResponse()
msg = resp.message()
responded = False
if 'quote' in incoming_msg:
# return a quote
r = requests.get('https://api.quotable.io/random')
if r.status_code == 200:
data = r.json()
quote = f'{data["content"]} ({data["author"]})'
else:
quote = 'I could not retrieve a quote at this time, sorry.'
msg.body(quote)
responded = True
if 'cat' in incoming_msg:
# return a cat pic
msg.media('https://cataas.com/cat')
responded = True
if not responded:
msg.body('I only know about famous quotes and cats, sorry!')
return str(resp)
if __name__ == '__main__':
app.run()
The app is running on http://127.0.0.1:5000/
When I click this link this is what I see
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Installed and run ngrok. I pasted the url under forwarding (there are two I copied the first), pasted this url to my Twilio sandbox (when a message comes in URL) and added /bot to the path.
However when I try and type cat on whatsapp (I have added my whatsapp number to my sandbox) I do not get a response (from my understanding I should be getting a picture of a cat if I type the keyword cat).
Not sure what I am doing wrong.
I copied the same code here from the blog here (is this the blog you used), Build a WhatsApp Chatbot With Python, Flask and Twilio, and updated the WhatsApp sandbox When A Message Comes In URL to point to the Ngrok URL which exposes it to the Internet and it works.
Can you check your Twilio Debugger, to see if there were any errors?
You cannot use your web browser to visit the Flask URL, since it is expecting an HTTP POST and your browser is using an HTTP GET. You can use a tool like Postman to do a POST to your URL, and see what response you get.
You should get this response when visit the URL and sending in the Body POST parameter of cat.
You could also check the Ngrok debug URL, http://127.0.0.1:4040/, to see what you are getting from Twilio when you send an inbound WhatsApp sandbox message, and debug from there.

Can't send http request using python, only sends to https

I have the request below:
#app.route('/solve', methods=['POST'])
def solve():
if request.method == "POST":
token = request.form.get('g-recaptcha-response', '')
print('Posted Token : ' + token)
Thread(target = tokenremoval, args = [token]).start()
return('Success')
I want to send this post request to http://127.0.0.1:5000/solve , but it seems like it is sending to https://127.0.0.1:5000/solve. When I make the post request it sends it to that link above and i get:
This site can’t provide a secure connection
127.0.0.1 sent an invalid response.
Try running Windows Network Diagnostics.
ERR_SSL_PROTOCOL_ERROR
Why can't i send the post request to the http link?

How can I add plain text as a response body to a HttpResponse in Python?

I'm trying to develop a Slack App, and I've currently got it working so that a payload is sent to my webserver, some data is processed and I now want to return the HTTP 200 response required but with a response message included. Here is the example they use.
At the moment I just have return HttpResponse(status=200)
How can I go about doing this?

Streamlabs API 405 response code

I'm trying to use Streamlabs API. Streamlabs API uses Oauth2 for creating apps. So first I send whoever's using my app to an authorization link containing my app's client id and the scopes I want to use.
(Something like this: streamlabs.com/api/v1.0/authorize?client_id=CLIENT-ID-HERE&redirect_uri=REDIRECT-URI&response_type=code&scope=SOME+SCOPES+HERE)
Once I've done that I receive a code at the redirect uri specified. I then use that code to get the access token for permanent access to the connected user's account. I then receive the access token from a POST request that works perfectly... Now I run into the problem. When getting the temporary code before the access token I specified the scopes: "donations.read +donations.create+alerts.write+alerts.create".
When authorizing, the app asks for permission to the different scopes. The scope in focus is "alerts.write" so that I can send test alerts using POST requests. But this doesn't work for some reason. To send a test alert I have to send a POST request to this url: "https://streamlabs.com/api/alerts/send_test_alert"
I've tried doing that in two different ways.
1:
import requests
url = "https://streamlabs.com/api/alerts/send_test_alert"
data = {
"access_token":"UserAccessTokenHere",
"type":"donation"
}
response = requests.post(url=url, data=data)
print(response.text)
2:
import requests
url = "https://streamlabs.com/api/alerts/send_test_alert?access_token=UserAccessTokenHere&type=donation"
response = requests.post(url=url)
print(response.text)
If I do print(response) it prints "Response [405]".
But if I do print(response.text) I get a long HTML document for this page: Error response page
Any ideas what's going wrong with my Python requests? send_test_alert documentation here: Link
I've contacted support and looks like you've made the same error as me.
You're not actually sending a request to the right URL.
You are a sending a request to: "https://streamlabs.com/api/alerts/send_test_alert"
You should be using the URL: "https://streamlabs.com/api/v1.0/alerts/send_test_alert"

Sendgrid inbound webhook data entirely blank in Flask but works with a request sent from requests?

I have a heroku flask app up right now. When it receives a POST request, it uses the sendgrid API to send an email to my personal email containing the POST data. Relevant code (driver.sendgridemail is the example code for python written with https://github.com/sendgrid/sendgrid-python) with a string passed as an argument:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
data = request.data.decode('utf-8')
driver.sendgridemail("Data is here: %s" % (data))
return data
else:
return render_template("index.html")
When I POST using requests.post:
requests.post("my_heroku_url", data=json.dumps({"some":"data"}), headers={"Content-Type":"application/json"})
I get an email to my personal email containing "Data is here: {"some":"data"}"
However, when I use the sendgrid inbound parse feature, the only email forwarded to my personal email is "Data is here:" which is obviously not ideal.
Does it have to do with content-type? Why isn't it processing the data from incoming email messages? It's obviously getting the email because it's capable of being forwarded to my personal email per flask app, it just doesn't retrieve the data. In other words, the POST request goes through but it's unable to retrieve any data and gives me a blank binary string. Finally, isn't it a little weird that is still thinks it's b" ? Does that mean it never gets decoded to a string with the sendgrid parse, but it does with a python requests post?
According to the docs:
The request that will be sent to the HTTP endpoint will be encoded as multipart/form-data.
You need to use the request.form dict to access form-data in Flask.
A tutorial is available on Sendgrid's blog: https://sendgrid.com/blog/collect-inbound-email-using-python-and-flask/
Additionally, when you make that change, you should remove headers={"Content-Type":"application/json"} from your requests.post call, so that you send it as form-data too.
Had the same problem but requested a form which I converted to a dict. The webhook now works like a charm. I also recommend you use ngrok to test the endpoint as sendgrid requires you to expose the url.
data = request.form.to_dict(flat=False)

Categories