I am in the middle of creating todo app integreted with Slack. I need to use dialog.open property of slack.
I managed to go through slack api tutorial however can not finally understand how dialogs work in integration with external systems. I created code which runs after slash command in slack. It should open dialog and show it to user, however it doesn't. I printed some parts of code to see what happens inside - looks like whole code works and server returns 200.
#app.route('/helpdesk', methods=['POST'])
def helpdesk():
print(request.form)
api_url = 'https://slack.com/api/dialog.open'
user_id = request.form['user_id']
trigger_id = request.form['trigger_id']
dialog = {
"token": "J1llSAeQAxNyw8yc37xuEsad",
"trigger_id": trigger_id,
"dialog": {
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"notify_on_cancel": True,
"state": "Limo",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
}
}
print(dialog)
requests.post(api_url, data=dialog)
return make_response()
I expect to see dialog window after writing slash command in slack.
What I see in prints:
ImmutableMultiDict([('token', 'J1llSAeQAxNyw8yc37xuEsad'), ('team_id', 'TKWQ5QP7Y'), ('team_domain', 'team-learningslack'), ('channel_id', 'CKH7RSZPC'), ('channel_name', 'slackflask'), ('user_id', 'UKN9KU7JM'), ('user_name', 'konrad.marzec1991'), ('command', '/musi'), ('text', ''), ('response_url', 'https://hooks.slack.com/commands/TKWQ5QP7Y/664885241506/ABjpMYmTWrnXpSBoGMpaJtOV'), ('trigger_id', '669947662833.676821839270.6c4bddd1418d3d4f2c8626f7c9accdf7')])
{'token': 'J1llSAeQAxNyw8yc37xuEsad', 'trigger_id': '669947662833.676821839270.6c4bddd1418d3d4f2c8626f7c9accdf7', 'dialog': {'callback_id': 'ryde-46e2b0', 'title': 'Request a Ride', 'submit_label': 'Request', 'notify_on_cancel': True, 'state': 'Limo', 'elements': [{'type': 'text', 'label': 'Pickup Location', 'name': 'loc_origin'}, {'type': 'text', 'label': 'Dropoff Location', 'name': 'loc_destination'}]}}
127.0.0.1 - - [26/Jun/2019 00:15:35] "POST /helpdesk HTTP/1.1" 200 -
You had 2 issues in your code:
you need to use an access token, not a verification token in the call
to dialog.open
you need to send the dialog definition as JSON, not as as form array
I made these additional changes
- Added code for using a slack token defined as environment variable
- Use the get() method to access form parameters in from the request
- Added code to show the API response from dialog.open
Here is a corrected version of your code:
import os
import requests
from flask import Flask, json, request
app = Flask(__name__) #create the Flask app
#app.route('/helpdesk', methods=['POST'])
def helpdesk():
api_url = 'https://slack.com/api/dialog.open'
trigger_id = request.form.get('trigger_id')
dialog = {
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"notify_on_cancel": True,
"state": "Limo",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
}
api_data = {
"token": os.environ['SLACK_TOKEN'],
"trigger_id": trigger_id,
"dialog": json.dumps(dialog)
}
res = requests.post(api_url, data=api_data)
print(res.content)
return make_response()
if __name__ == '__main__':
app.run(debug=True, port=8000) #run app in debug mode on port 8000
Related
I'm trying to marshal a simple data structure in Flask, as below:
{
"globalNum": 1.23,
"perResultData": [
{
"string1": "test string",
"num1": 1.25
},
{
"string1": "test",
"num1": 1.22
}
]
}
I'm modelling that structure like this:
testmodel = api.model('Model', {
'globalNum': fields.Float,
'perResultData': fields.List(fields.Nested({
"string1": fields.String,
"num1": fields.Float
}))
})
When I try this setup (as per minimum failing code below), if I browse to localhost I get a warning 'No API definition provided.' and the Flask console shows:
File "/home/mikea/.local/lib/python3.6/site-packages/flask_restplus/swagger.py", line 574, in register_model
if name not in self.api.models:
TypeError: unhashable type: 'dict'
Flask works perfectly when I comment out the '#api.marshal_with(testmodel)' line.
Can someone shed some light on what I'm doing wrong, please? Thanks very much
Full code:
from flask_restplus import Resource, Api,fields
app = Flask(__name__)
api = Api(app)
testmodel = api.model('Model', {
'globalNum': fields.Float,
'perResultData': fields.List(fields.Nested({
"string1": fields.String,
"num1": fields.Float
}))
})
#api.route('/')
class incomingRequest(Resource):
#api.marshal_with(testmodel)
def post(self):
return {"globalNum":3.2,
"perResultData":[
{
"string1": "test string",
"num1": 1.25
},
{
"string1": "test",
"num1": 1.22
}
]}
if __name__ == '__main__':
app.run(debug=True)
The answer was here - https://github.com/noirbizarre/flask-restplus/issues/292
When nesting models, you have to wrap them in the model class, like the below:
testmodel = api.model('Model', {
'globalNum': fields.Float,
'perResultData': fields.List(fields.Nested(api.model({
"string1": fields.String,
"num1": fields.Float
})))
})
I'm creating a Slack app using Python and Flask. The app uses a Slack modal with two views: The first is opened when a slash command is given and the second should be opened when the user presses the submit button on the first view. When I run this, the first view opens correctly. However, when I try to push the second view it opens and then closes immediately, reverting back to the first view without any interaction from the user.
I have tried using the notify_on_close flag to see if the second view is being closed for some reason, however I am not getting any 'form closed' messages after the second view closes.
Here is an example of where this problem occurs:
#app.route('/slash_command', methods=['POST'])
def open_modal():
trigger_id = request.form['trigger_id']
sc.views_open(trigger_id=trigger_id, view=views.first_view)
return '', 200
#app.route('/actions', methods=['POST'])
def action_endpoint():
payload = json.loads(request.form['payload'])
callback_id = payload['view']['callback_id']
trigger_id = payload['trigger_id']
# Push the second view if the first view is submitted
if callback_id == 'first_view':
sc.views_push(trigger_id=trigger_id, view=views.second_view)
return '', 200
My views are very simple:
first_view = {
"type": "modal",
'callback_id': 'first_view',
"title": {
"type": "plain_text",
"text": "First View"
},
"submit": {
"type": "plain_text",
"text": "Submit"
},
"close": {
"type": "plain_text",
"text": "Cancel"
},
'blocks': []
}
second_view = {
'type': 'modal',
'callback_id': 'second_view',
'title': {
'type': 'plain_text',
'text': 'Second View',
},
'submit': {
'type': 'plain_text',
'text': 'Submit'
},
'close': {
'type': 'plain_text',
'text': 'Cancel'
},
'blocks': []
}
Slack support resolved this by explaining that the above code pushes a new view and then returns a 200 response, which is interpreted by Slack as a request to close the current view. I was able to make the code work correctly by updating it as follows:
# Push the second view if the first view is submitted
if callback_id == 'first_view':
return {
'response_action': 'push',
'view': views.second_view
}
return '', 200
I wrote a python code for the dialog flow using the flask and webhook. I am able to get the response but it not displayed in the dialog flow. This code running perfectly.
CODE:
# import os
import json
# import urllib
import datetime
from config import Configuration
from swe_commands import SweCommands
from flask import Flask, request, make_response
# Flask application should start in global layout
app = Flask(__name__)
#app.route('/webhook', methods=['POST'])
def webhook():
req = request.get_json(silent=True, force=True)
print "Request:"
print json.dumps(req, indent=1)
res = make_webhook_result(req)
res = json.dumps(res, indent=1)
print "Response:"
print res
r = make_response(res)
r.headers['Content-Type'] = 'application/json'
return r
def make_webhook_result(req):
# if req.get("queryResult").get("action") != "nakshatra":
# return {}
swe_path = Configuration.swe_path()
date_and_time = str(datetime.datetime.now())[:19]
panchang_dictionary = SweCommands.find_panchang(swe_path, date_and_time)
result = req.get("queryResult")
parameters = result.get("parameters")
angam = parameters.get("nakshatra")
nakshatra = panchang_dictionary[angam]
speech = "Current nakshatra is %s" % nakshatra
source = "Panchangam"
output = {'speech': speech, "displayText": speech, "source": source}
return output
if __name__ == '__main__':
port = 5000
print "Starting app in port %s" % port
app.run(debug=True, port=port, host='127.0.0.1')
REQUEST:
**{
"queryResult": {
"fulfillmentMessages": [
{
"text": {
"text": [
""
]
}
}
],
"allRequiredParamsPresent": true,
"parameters": {
"nakshatra": "nakshatra"
},
"languageCode": "en",
"intentDetectionConfidence": 0.6725314,
"action": "nakshatra",
"intent": {
"displayName": "PanchangFind",
"name": "projects/my-project-1536557558293/agent/intents/6d1d46bf-3787-48cd-9b45-0766d5f2b107"
},
"queryText": "What is nakshatra"
},
"originalDetectIntentRequest": {
"payload": {}
},
"session": "projects/my-project-1536557558293/agent/sessions/08857865-1d08-2eef-5d4f-83b92107f09b",
"responseId": "2137da9d-23a9-4735-aec2-7adb7ae52d85-9cc28bb4"
}**
RESPONSE:
**{
"displayText": "Current nakshatra is Shravana",
"speech": "Current nakshatra is Shravana",
"source": "Panchangam"
}**
But it not get displayed in the dialog flow panel? Whether I have to set any parameters for dialog flow panel to receive the response in a dialog flow. Please let me know.
I got an answer to this. When we use V1 in dialog flow, the response key is displayText. But in V2 the response key is fulfillmentText. When I added the response key in this name it able to detect the output.
I'm trying to hit my geocoding server's REST API:
[https://locator.stanford.edu/arcgis/rest/services/geocode/USA_StreetAddress/GeocodeServer] (ArcGIS Server 10.6.1)
...using the POST method (which, BTW, could use an example or two, there only seems to be this VERY brief "note" on WHEN to use POST, not HOW: https://developers.arcgis.com/rest/geocode/api-reference/geocoding-geocode-addresses.htm#ESRI_SECTION1_351DE4FD98FE44958C8194EC5A7BEF7D).
I'm trying to use requests.post(), and I think I've managed to get the token accepted, etc..., but I keep getting a 400 error.
Based upon previous experience, this means something about the formatting of the data is bad, but I've cut-&-pasted directly from the Esri support site, this test pair.
# import the requests library
import requests
# Multiple address records
addresses={
"records": [
{
"attributes": {
"OBJECTID": 1,
"Street": "380 New York St.",
"City": "Redlands",
"Region": "CA",
"ZIP": "92373"
}
},
{
"attributes": {
"OBJECTID": 2,
"Street": "1 World Way",
"City": "Los Angeles",
"Region": "CA",
"ZIP": "90045"
}
}
]
}
# Parameters
# Geocoder endpoint
URL = 'https://locator.stanford.edu/arcgis/rest/services/geocode/USA_StreetAddress/GeocodeServer/geocodeAddresses?'
# token from locator.stanford.edu/arcgis/tokens
mytoken = <GeneratedToken>
# output spatial reference id
outsrid = 4326
# output format
format = 'pjson'
# params data to be sent to api
params ={'outSR':outsrid,'f':format,'token':mytoken}
# Use POST to batch geocode
r = requests.post(url=URL, data=addresses, params=params)
print(r.json())
print(r.text)
Here's what I consistently get:
{'error': {'code': 400, 'message': 'Unable to complete operation.', 'details': []}}
I had to play around with this for longer than I'd like to admit, but the trick (I guess) is to use the correct request header and convert the raw addresses to a JSON string using json.dumps().
import requests
import json
url = 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/Locators/SanDiego/GeocodeServer/geocodeAddresses'
headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
addresses = json.dumps({ 'records': [{ 'attributes': { 'OBJECTID': 1, 'SingleLine': '2920 Zoo Dr' }}] })
r = requests.post(url, headers = headers, data = { 'addresses': addresses, 'f':'json'})
print(r.text)
I'm trying to make a google home assistant that just parrots back whatever a user says to it. Basically I need to capture what the user is saying, and then feed it back into a response.
I have some puzzle pieces figured out.
One is initializing the API to do queries:
api = ApiAi(os.environ['DEV_ACCESS_TOKEN'], os.environ['CLIENT_ACCESS_TOKEN'])
The other is a fallback intent that is intended to just capture whatever the user says and repeat it back:
#assist.action('fallback', is_fallback=True)
def say_response():
""" Setting the fallback to act as a looper """
speech = "test this" # <-- this should be whatever the user just said
return ask(speech)
Another is the JSON response on the API.AI site looks like this:
{
"id": "XXXX",
"timestamp": "2017-07-20T14:10:06.149Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "ok then",
"action": "say_response",
"actionIncomplete": false,
"parameters": {},
"contexts": [],
"metadata": {
"intentId": "a452b371-f583-46c6-8efd-16ad9cde24e4",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "true",
"webhookResponseTime": 112,
"intentName": "fallback"
},
"fulfillment": {
"speech": "test this",
"source": "webhook",
"messages": [
{
"speech": "test this",
"type": 0
}
],
"data": {
"google": {
"expect_user_response": true,
"is_ssml": true
}
}
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "XXXX"
}
The module I'm intializing from looks like this: https://github.com/treethought/flask-assistant/blob/master/api_ai/api.py
Full program looks like this:
import os
from flask import Flask, current_app, jsonify
from flask_assistant import Assistant, ask, tell, event, context_manager, request
from flask_assistant import ApiAi
app = Flask(__name__)
assist = Assistant(app, '/')
api = ApiAi(os.environ['DEV_ACCESS_TOKEN'], os.environ['CLIENT_ACCESS_TOKEN'])
# api.post_query(query, None)
#assist.action('fallback', is_fallback=True)
def say_response():
""" Setting the fallback to act as a looper """
speech = "test this" # <-- this should be whatever the user just said
return ask(speech)
#assist.action('help')
def help():
speech = "I just parrot things back!"
## a timeout and event trigger would be nice here?
return ask(speech)
#assist.action('quit')
def quit():
speech = "Leaving program"
return tell(speech)
if __name__ == '__main__':
app.run(debug=False, use_reloader=False)
How do I go about getting the "resolvedQuery" out of the JSON to be fedback as "speech" for the response?
Thanks.
The flask_assistant library does a good job of parsing the request into a dict object.
You can get the resolvedQuery by writing:
speech = request['result']['resolvedQuery']
Just create a new intent (doesn’t matter the name) and a template with sys.any; after that go on your server and use something similar to the following code
userInput = req.get(‘result’).get(‘parameters’).get(‘YOUR_SYS_ANY_PARAMETER_NAME’)
Then send userInput back as the speech response.
Something like this is how you get the initial JSON data:
#app.route(’/google_webhook’, methods=[‘POST’])
def google_webhook():
# Get JSON request
jsonRequest = request.get_json(silent=True, force=True, cache=False)
print("Google Request:")
print(json.dumps(jsonRequest, indent=4))
# Get result
appResult = google_process_request(jsonRequest)
appResult = json.dumps(appResult, indent=4)
print("Google Request finished")
# Make a JSON response
jsonResponse = make_response(appResult)
jsonResponse.headers['Content-Type'] = 'application/json'
return jsonResponse