Make Slack API buttons "do something" - python

Edit: Since I asked this question I have created a ngrok URL to receive the Slack POST when the user his the button.
However, every time I press the button I get this: "POST / HTTP/1.1" 404 -
I am using a local Flask URL, code below:
from flask import Flask, request
app = Flask(__name__)
#app.route('/payload', methods=['POST'])
def incoming_slack_message():
req = request.get_json(Force=True)
info = request.form['channel_id']
print(req)
print(info)
print('did it work???')
return 'action successful'
#app.route('/slack/blocks', methods=['POST'])
def incoming_slack_options():
req = request.get_json(Force=True)
info = request.form['payload']
print(req)
print(info)
print('Did it work here??')
return 'ok'
if __name__ == '__main__':
app.run(port=3000, debug = True)
I have read that I need to include a callback_id in the block but whenever I do I get TypeError: 'NoneType' object is not subscriptable
This is the block I am using less the callback_id
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Once your machine is selected, click here."
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Change",
},
"value": "click_me_123",
"action_id": "button"
}
}
I am SURE something is wrong in my flask code as I know little to nothing about Flask. I don't think there's anything wrong with my Slack Block but I still feel there should be a callback_id. Thank you for your time.

Okay I got the button to generate a response! The Flask code is as follows
from flask import Flask, request, Response, jsonify
import requests
import json
app = Flask(__name__)
#app.route('/', methods=['POST'])
def resp():
data = request.json
button_info = request.form['payload']
webhook_url = 'webhook from slack'
slack_data = { 'text': "How can I help you?"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
return(jsonify(data), print(button_info))
if __name__ == '__main__':
app.run(port=3000, debug = True)
The button_info will print out all the data.
I'm sure I can get rid of some of these line/imports and it will still work but this will get the job done!
*** This code is in conjunction with a ngrok public URL for the local web server.
brew cask install ngrok followed by
ngrok http 3000 to match the 3000 port in my code
Thewebhook_url is from the slack API website.

Related

APNS Notifications: Python HTTP/2 POST request returning 404 ({"reason":"BadPath"})

I am developing a watchOS app and I want to send push notifications to my users. I have enabled "Push Notifications" in signing and capabilities and a file called "app_name WatchKit Extension" was generated containing one entitlement called APS environment whose value is set to "development".
I have also generated a .p8 file in the Apple Developer Website with the authentication key and that also gives me the key id.
I have created a Swift class called App Delegate that conforms to UNUserNotificationCenterDelegate. I have implemented the method applicationDidFinishLaunching from where I call WKExtension.shared().registerForRemoteNotifications(). Then I implemented the didRegisterForRemoteNotifications where I receive the device token and convert it into a string by executing these lines of code:
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
Then I send the token to the server. In the server I have a Python script that makes the post request to https://api.sandbox.push.apple.com:443 with the headers and notification payload. However, I always get a 404 error ({"reason":"BadPath"}).
I don't know what I am doing wrong. Am I configuring anything wrong? This is the Python script I am using:
import time
import httpx
import asyncio
import jwt
ALGORITHM = 'ES256'
APNS_AUTH_KEY = "path to .p8 file"
f = open(APNS_AUTH_KEY)
secret = f.read()
apns_token = jwt.encode(
{
'iss': 'cert_id',
'iat': time.time()
},
secret,
algorithm=ALGORITHM,
headers={
'alg':ALGORITHM,
'kid':'key_id'
}
)
dev_server = "https://api.sandbox.push.apple.com:443"
device_token = "9fe2814b6586bbb683b1a3efabdbe1ddd7c6918f51a3b83e90fce038dc058550"
headers = {
'method': 'POST',
'path': '/3/device/{0}'.format(device_token),
'autorization': 'bearer {0}'.format(apns_token),
'apns-push-type': 'myCategory',
'apns-expiration': '0',
'apns-priority': '10',
}
payload = {
"aps" : {
"alert" : {
"title" : "Hello Push",
"message": "This is a notification!"
},
"category": "myCategory"
}
}
async def test():
async with httpx.AsyncClient(http2=True) as client:
client = httpx.AsyncClient(http2=True)
r = await client.post(dev_server, headers=headers, data=payload)
print(r.text)
print(r)
asyncio.run(test())
Is there anything wrong with the way I am setting things up or performing the post request?
Thank you for your help!

How to do transition to another flow in dialogflow CX and get responce from the target flow

lets say i have 2 flows "Book appointment" and "User registration". I need to do transition from "Book appointment" Flow to "User registration" flow. I am able to do transition here but i am not getting the response from "User Registration" flow.
I have enabled the webhook in both flows.
How to get response from User registration as soon as this flow gets triggered.
let me know if there is any other possible way achieve this, but i need to do transition from backend only
Thanks in advance
import json
from flask import Flask
from flask import Response, request
import requests
app = Flask(__name__)
#app.route('/transition_userRegistration', methods=['GET', 'POST'])
def transition_userRegistration():
body = request.get_json(silent=True)
fulfillment = body['fulfillmentInfo']['tag']
print('fulfillment', fulfillment)
user_txt = body['text']
Page_ans =page_transition(user_txt)
return Page_ans
def page_transition(msg):
message= {"fulfillment_response": {
"messages": []
},
"target_flow": 'FlowID path',
}
if isinstance(msg, list):
for i in msg:
message["fulfillment_response"]["messages"].append({
"text": {
"text": [i]
}
})
else:
message["fulfillment_response"]["messages"].append({
"text": {
"text": [msg]
}
})
print('message from page', message)
return Response(json.dumps(message), 200, mimetype='application/json')
if __name__ == '__main__':
app.run(debug=True)

Flask API returns Null for simple input

I'm new to this, but I have started to learn how to make Flask API for ML.
I have wrote a code that should just return inputed text, but it returns Null. This is my code:
from flask import Flask
from flask_restful import Api, Resource, reqparse
APP = Flask(__name__)
API = Api(APP)
class Predict(Resource):
#staticmethod
def post():
json_data = reqparse.RequestParser()
json_data.add_argument('Title')
json_data.add_argument('Text')
json_data = json_data.parse_args() # creates dict
print("INPUT DATA", json_data)
return json_data, 200
API.add_resource(Predict, '/predict')
if __name__ == '__main__':
APP.run(debug=True, port='1180')
My API link is: http://127.0.0.1:1180/predict
I have tried to test in in Postman, I set POST request, added API link, and in Body field I added this:
{
"Title": "test1",
"Text": "test2"
}
My result is this:
{
"Title": null,
"Text": null
}
Why Im getting this result?
I tested it with python module requests
Code works for me if I send it as FORM data - data=data
import requests
data = {
"Title": "test1",
"Text": "test2"
}
r = requests.post('http://localhost:1180/predict', data=data)
print(r.text)
print(r.json())
It also works if I send it as JSON data - json=data
import requests
data = {
"Title": "test1",
"Text": "test2"
}
r = requests.post('http://localhost:1180/predict', json=data)
print(r.text)
print(r.json())
It gives null only if I send it as raw text instead of FORM data - data='''{ text }'''
import requests
data = '''{
"Title": "test1",
"Text": "test2"
}'''
r = requests.post('http://localhost:1180/predict', data=data)
print(r.text)
print(r.json())
And if send it as raw text instead of JSON data - json='''{ text }''' -
then I get {"message": "Internal Server Error"}
import requests
data = '''{
"Title": "test1",
"Text": "test2"
}'''
r = requests.post('http://localhost:1180/predict', json=data)
print(r.text)
print(r.json())
It may means you put data in Postman in wrong format.

How to make a simple Python REST server and client?

I'm attempting to make the simplest possible REST API server and client, with both the server and client being written in Python and running on the same computer.
From this tutorial:
https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask
I'm using this for the server:
# server.py
from flask import Flask, jsonify
app = Flask(__name__)
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
#app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
if __name__ == '__main__':
app.run(debug=True)
If I run this from the command line:
curl -i http://localhost:5000/todo/api/v1.0/tasks
I get this:
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 317
Server: Werkzeug/0.16.0 Python/3.6.9
Date: Thu, 05 Mar 2020 02:45:59 GMT
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}
Great, now my question is, how can I write a Python script using requests to obtain the same information?
I suspect this is the proper idea:
# client.py
import requests
url = 'http://todo/api/v1.0/tasks'
response = requests.get(url,
# what goes here ??
)
print('response = ' + str(response))
However as you can see from my comment, I'm not sure how to set up the parameters for requests.get.
I attempted to use this SO post:
Making a request to a RESTful API using python
as a guideline however it's not clear how to adjust the formatting per the message change.
Can provide a brief description of how to set up params to pass into requests.get and suggest the necessary changes to get the client example above working? Thanks!
--- Edit ---
Something else I can mention is that I got the client to work using Postman pretty easily, I'm just not sure how to set up the syntax in Python:
--- Edit ---
Based on icedwater's response below, this is complete, working code for the client:
# client.py
import requests
import json
url = 'http://localhost:5000/todo/api/v1.0/tasks'
response = requests.get(url)
print(str(response))
print('')
print(json.dumps(response.json(), indent=4))
result:
<Response [200]>
{
"tasks": [
{
"description": "Milk, Cheese, Pizza, Fruit, Tylenol",
"done": false,
"id": 1,
"title": "Buy groceries"
},
{
"description": "Need to find a good Python tutorial on the web",
"done": false,
"id": 2,
"title": "Learn Python"
}
]
}
From help(requests.get):
Help on function get in module requests.api:
get(url, params=None, **kwargs)
Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
so I would say requests.get(url) would be enough to get a response. Then look at either the json() or data() functions in response depending on what the API is expected to return.
So for the case of a JSON response, the following code should be enough:
import requests
import json
url = "https://postman-echo.com/get?testprop=testval"
response = requests.get(url)
print(json.dumps(response.json(), indent=4))
Try the above code with an actual test API.

I only get one whatsapp answer via dialogflow instead of multiple replies

How do I get multiple 'text answers' in whatsApp? When I add more than one 'text response' in intents they work normally in the dialogFlow console.
But when I repeat the same question on whatsapp I get only one answer box instead of 3, for example, that I had created.
I am using twilio to communicate with the whatsapp API. I also use Horoku cloud services to host the application.
Everything works normal. But I received only one message box instead of multiple in whatsapp.
I think the problem is my python code 'app.py'.
app.py
#app.route("/") #just to test Heroku cloud services
def hello():
return "Hello, World!"
#app.route("/sms", methods=['POST'])
def sms_reply():
"""Respond to incoming calls with a simple text message."""
# Fetch the message
msg = request.form.get('Body')
phone_no = request.form.get('From')
reply = fetch_reply(msg, phone_no)
# Create reply
resp = MessagingResponse()
resp.message(reply)
enter code here
return str(resp)
utils.py
import dialogflow_v2 as dialogflow
dialogflow_session_client = dialogflow.SessionsClient()
PROJECT_ID = "weather-husgcf"
def detect_intent_from_text(text, session_id, language_code='pt-BR'):
session = dialogflow_session_client.session_path(PROJECT_ID, session_id)
text_input = dialogflow.types.TextInput(text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = dialogflow_session_client.detect_intent(session=session, query_input=query_input)
return response.query_result
def fetch_reply(query, session_id):
response = detect_intent_from_text(query, session_id)
return response.fulfillment_text
https://i.imgur.com/a/b2QSYUB "ScreenShots"
Twilio developer evangelist here.
In your fetch_reply method you call on the query_result's fulfillment_text property. According the QueryResult documentation fulfillmentText is deprecated/legacy:
The text to be pronounced to the user or shown on the screen. Note: This is a legacy field, fulfillmentMessages should be preferred.
The fullfillmentMessages property is defined as a list of Message objects. So to return all 3 of your messages your code should probably loop through the messages adding them to the response, something like this:
def fetch_reply(query, session_id):
response = detect_intent_from_text(query, session_id)
return response.fulfillment_messages
Then your route should look something like this:
#app.route("/sms", methods=['POST'])
def sms_reply():
"""Respond to incoming calls with a simple text message."""
# Fetch the message
msg = request.form.get('Body')
phone_no = request.form.get('From')
replies = fetch_reply(msg, phone_no)
# Create reply
resp = MessagingResponse()
for reply in replies:
resp.message(reply.text)
return str(resp)
I haven't tested this, just worked from the DialogFlow documentation. Let me know if it helps.
Old-ish question but here's the answer (Dialogflow v2).
Assuming you have some sort of sendMessage(mobile_num, text) function, you iterate over the fulfillment_messages like this:
for message in response.query_result.fulfillment_messages:
sendMessage(mobile_num, message.text.text[0])
From the webhook you get a json like this:
{
"queryText": string,
"languageCode": string,
"speechRecognitionConfidence": number,
"action": string,
"parameters": {
object
},
"allRequiredParamsPresent": boolean,
"cancelsSlotFilling": boolean,
"fulfillmentText": string,
"fulfillmentMessages": [
{
"text": {
"text": [
"Some text"
]
}
},
{
"text": {
"text": [
"Some more text"
]
}
},
],
"webhookSource": string,
"webhookPayload": {
object
},
"outputContexts": [
{
object (Context)
}
],
"intent": {
object (Intent)
},
"intentDetectionConfidence": number,
"diagnosticInfo": {
object
},
"sentimentAnalysisResult": {
object (SentimentAnalysisResult)
}
}
where fulfillmentMessages is an array you have to iterate over.
Hope this helps.

Categories

Resources