I want to create a chatbot with Dialogflow and Google Assistant along with Google Transactions API for enabling a user to order a chocolate box. For now my agent contains the following four intents:
Default Welcome Intent (text response: Hello, do you want to buy a chocolate box?)
Default Fallback Intent
Int1 (training phrase: Yes, I want, fulfilment: enabled webhook call)
Int2 (event: actions_intent_TRANSACTION_REQUIREMENTS_CHECK )
I am using Dialogflow Json instead of Node.js to connect my agent with Transactions API. I want to present the order preview (when ordering the chocolate box) by using the actions.intent.TRANSACTION_REQUIREMENTS_CHECK action of Google actions. For this reason, following Google docs, when Int1 is triggered I am using a webhook which connect Google Assistant to the following python script (back-end):
from flask import Flask, render_template, request, jsonify
import requests
app = Flask(__name__)
#app.route("/", methods=['POST'])
def index():
data = request.get_json()
intent = data["queryResult"]["intent"]["displayName"]
if (intent == 'Int1'):
proposedOrder = order.proposed_order(location)
return jsonify({
"fulfillmentText": "This is your order preview:",
"payload": {
"google": {
"expectUserResponse": True,
"isSsml": False,
"noInputPrompts": [],
"systemIntent": {
"data": {
"#type": "type.googleapis.com/google.actions.v2.TransactionDecisionValueSpec",
"orderOptions": {
"requestDeliveryAddress": True,
},
"paymentOptions": {
"actionProvidedOptions": {
"displayName": "VISA **** **** **** 3235",
"paymentType": "PAYMENT_CARD"
}
},
"proposedOrder": proposedOrder
},
"intent": "actions.intent.TRANSACTION_DECISION"
}
}
}
})
if __name__== "__main__":
app.run(debug=True)
where proposed_order is a function which I wrote in the module order which forms the order of the user in the required way specified by Google docs.
When intent == 'Int1' then this will present the order preview to the user (on mobile phone Google Assistant) which looks like this (the example is from Google docs):
As you can see there are three chip suggestions at the bottom of the order preview: Place order, Change payment method, Never mind.
My question is the following: How can I (programmatically) edit these chip suggestions and add mine (e.g. add one chip suggestion 'Change number of items ordered'?
Within the platform's order form, you do not have additional control over what options the user sees. You'd want to have add an intermediary step of the conversation to give them a pre-final check before sending the transaction intent.
Related
Campaign Monitor is a service where we can send emails to a set of subscribers. We can create multiple lists within Campaign Monitor and add the required users to these lists as subscribers(to whom we can send personalised emails). So, here I am trying to send a set of customers' details like their name, emails, total_bookings, and first_booking to the campaign monitor's list using the API in Python so that I can send emails to this set of users.
More details on campaign monitor: https://www.campaignmonitor.com/api/v3-3/subscribers/
I am new to using Campaign Monitor. I have searched documentation, a lot of posts and blogs for examples on how to push data with multiple custom fields to Campaign Monitor using Python. By default, a list in Campaign Monitor will have a name and an email that can be added, but I want to add other details for each subscriber(here I want to add total_bookings and first_booking data) and Campaign Monitor provides custom fields to achieve this.
For instance:
I have my data stored in a redshift table named customer_details with the fields name, email, total_bookings, first_booking. I was able to retrieve this data from redshift table using Python with the following code.
# Get the data from the above table:
cursor = connection.cursor()
cursor.execute("select * from customer_details")
creator_details = cursor.fetchall()
# Now I have the data as a list of sets in creator_details
Now I want to push this data to a list in the Campaign Monitor using API like request.put('https://api.createsend.com/api/../.../..'). But I am not sure on how to do this. Can someone please help me here.
400 indicated invalid parameters
we can first see the request is POST not PUT
so first change requests.put to requests.post
the next thing is that all the variables need to be sent either as www-formdata or as json body data not sure which
and lastly you almost certainly cannot verify with basic auth ... but maybe
something like the following
some_variables = some_values
...
header = {"Authorization": f"Bearer {MY_API_KEY}"}
data = {"email":customer_email,"CustomFields":[{"key":"total_bookings","value":customer_details2}]}
url = f'https://api.createsend.com/api/v3.3/subscribers/{my_list_id}.json'
res = requests.post(url,json=data,headers=header)
print(res.status_code)
try:
print(res.json())
except:
print(res.content)
after looking more into the API docs it looks like this is the expected request
{
"EmailAddress": "subscriber#example.com",
"Name": "New Subscriber",
"MobileNumber": "+5012398752",
"CustomFields": [
{
"Key": "website",
"Value": "http://example.com"
},
{
"Key": "interests",
"Value": "magic"
},
{
"Key": "interests",
"Value": "romantic walks"
}
],
"Resubscribe": true,
"RestartSubscriptionBasedAutoresponders": true,
"ConsentToTrack":"Yes"
}
which we can see has "EmailAddress" not "email" so you would need to do
data = {"EmailAddress":customer_email,"CustomFields":[{"key":"total_bookings","value":customer_details2}]}
Im not sure if all of the fields are required or not ... so you may also need to provide "Name","MobileNumber",Resubscribe",etc
and looking at "Getting Started" it looks like the publish a python package to make interfacing simpler
http://campaignmonitor.github.io/createsend-python/
which makes it as easy as
import createsend
cli = createsend.CreateSend({"api_key":MY_API_KEY})
cli.subscriber.add(list_id,"user#email.com","John Doe",custom_fields,True,"No")
(which I found here https://github.com/campaignmonitor/createsend-python/blob/master/test/test_subscriber.py#L70)
I am trying to use the new API version V2 for IBM Cloud Watson Assistant. Instead of sending a message for a workspace I need to send a message to an assistant. The context structure has global and skill-related sections now.
How would my app pass in values as context variables? Where in the structure would they need to be placed? I am using the Python SDK.
I am interested in sending information as part of client dialog actions.
Based on testing the Python SDK and the API V2 using a tool, I came to the following conclusion. Context is provided by the assistant if it is requested as part of the input options.
"context": {
"skills": {
"main skill": {
"user_defined": {
"topic": "some chatbot talk",
"skip_user_input": true
}
}
},
"global": {
"system": {
"turn_count": 2
}
}
}
To pass back values from my client / app to the assistant, I could use the context parameter. However, in contrast to the V1 API I needed to place the key / value pairs "down below" in the user_defined part:
context['skills']['main skill']['user_defined'].update({'mydateOUT':'2018-10-08'})
The above is a code snippet from this sample file for a client action. With that placement of my context variables everything works and I can implement client actions using the API Version 2.
I'm trying to connect to a WAMP bus from a different application that has certain roles configured. The roles are authenticated with a static ticket, so I believe that I need to declare what role I want to connect as and what the associated ticket is. I'm writing this in Python and have most of the component set up, but I can't find any documentation about how to do this sort of authentication.
from autobahn.twisted.component import Component, run
COMP = Component(
realm=u"the-realm-to-connect",
transports=u"wss://this.is.my.url/topic",
authentication={
# This is where I need help
# u"ticket"?
# u"authid"?
}
)
Without the authentication, I'm able to connect to and publish to the WAMP bus when it is running locally on my computer, but that one is configured to allow anonymous users to publish. My production WAMP bus does not allow anonymous users to publish, so I need to authenticate what role this is connecting as. The Autobahn|Python documentation implies that it can be done in Python, but I've only been able to find examples of how to do it in JavaScript/JSON in Crossbar.io's documentation.
the documentation is not very up to date.
With the Component it is necessary to do like that for tickets:
from autobahn.twisted.component import Component, run
component = Component(
realm=u"the-realm-to-connect",
transports=u"wss://this.is.my.url/topic",
authentication={
"ticket": {
"authid": "username",
"ticket": "secrettoken"
}
},
)
Here is some example that can be helpful for you:
https://github.com/crossbario/crossbar-examples/tree/master/authentication
I think you need to use WAMP-Ticket Dynamic Authentication method.
WAMP-Ticket dynamic authentication is a simple cleartext challenge
scheme. A client connects to a realm under some authid and requests
authmethod = ticket. Crossbar.io will "challenge" the client, asking
for a ticket. The client sends the ticket, and Crossbar.io will in
turn call a user implemented WAMP procedure for the actual
verification of the ticket.
So you need to create an additional component to Authenticate users:
from autobahn.twisted.wamp import ApplicationSession
from autobahn.wamp.exception import ApplicationError
class AuthenticatorSession(ApplicationSession):
#inlineCallbacks
def onJoin(self, details):
def authenticate(realm, authid, details):
ticket = details['ticket']
print("WAMP-Ticket dynamic authenticator invoked: realm='{}', authid='{}', ticket='{}'".format(realm, authid, ticket))
pprint(details)
if authid in PRINCIPALS_DB:
if ticket == PRINCIPALS_DB[authid]['ticket']:
return PRINCIPALS_DB[authid]['role']
else:
raise ApplicationError("com.example.invalid_ticket", "could not authenticate session - invalid ticket '{}' for principal {}".format(ticket, authid))
else:
raise ApplicationError("com.example.no_such_user", "could not authenticate session - no such principal {}".format(authid))
try:
yield self.register(authenticate, 'com.example.authenticate')
print("WAMP-Ticket dynamic authenticator registered!")
except Exception as e:
print("Failed to register dynamic authenticator: {0}".format(e))
and add Authentication method in the configuration:
"transports": [
{
"type": "web",
"endpoint": {
"type": "tcp",
"port": 8080
},
"paths": {
"ws": {
"type": "websocket",
"serializers": [
"json"
],
"auth": {
"ticket": {
"type": "dynamic",
"authenticator": "com.example.authenticate"
}
}
}
}
}
]
I am building Alexa skill for my application. When your ask's 'what is my account status?' this intent return sequence of statements related to user's account. API gives following response
response = [{
..
text: 'Total orders are 41, Delivered 28'
..
},
{
..
text: 'Today orders are 12, Delivered 2'
..
},
{}]
How to build response sequence based on API response?
With this intent, I get the response from API with set statements Alexa should prompt each statement one by one. If the user said 'next' in between any of the statement while Alexa prompting then it goes to next statement in the response array.
First when user says "what is my account status?" your intent will be called and you will get response in a list where in the first call you will display 0th item.
API Result:
response = [{
..
text: 'Total orders are 41, Delivered 28'
..
},
{
..
text: 'Today orders are 12, Delivered 2'
..
},
{}]
You need to store information in Session attributes, like intent name, index which you displayed (0 in case of first call) etc.
Now you need to setup one more intent which will be triggered on keywords like next. In the code you will check values of session attributes and make your response according to the values. For example you would want to check previous intent name, previous index. If all is fine you will modify the session attributes and respond to user.
Hope it helps.
Since you mentioned Python, I would suggest to take a look at Flask-ask, which provides you with two main responses type: statement and question.
As sid8491 mentioned, you will need to store info in sessions to keep track of which response (from json) needs to be returned. You can use redis for this purpose, using this python library.
Assuming the json response is stored in db (or somewhere), and can be accessed in a list, let's say your interaction model looks something like this:
{
"languageModel": {
"intents": [
{
"name": "NextIntent",
"samples": ["next", "tell me more"]
},
{
"name": "StopIntent",
"samples": ["stop"]
},
{
"name": "StatusIntent",
"samples": ["what is my account status"]
}
],
"invocationName": "orders"
}
}
You can use following steps (using redis and flask-ask for this example):
on 'StatusIntent', store session and return first response:
redis.set("session_key", 0)
return statement(response[0]) # assuming responses are stored in a list
on 'NextIntent', get value stored in session, if present return next response
value = redis.get("session_key")
if not value: # session is expired
return statement("I don't understand")
redis.set("session_key", int(value)+1)
return statement(response[int(value)+1])
on 'StopIntent', remove "session_key" from redis
redis.delete("session_key")
return statement("Ok. I am here if you need me.")
It's not the actual code but simply intended to give you an idea. Hope it helps.
:)
I can not for the life of me understand why this isnt working.
Here's my lambda function
def lambda_handler(event, context):
url = "https://prod-65-19-131-166.wostreaming.net/kindred-wcmifmmp3-128"
return build_audio_response(url)
def build_audio_response(url):
return {
"version": "1.01",
"response": {
"directives": [
{
"type": "AudioPlayer.Play",
"playBehavior": "ENQUEUE",
"audioItem": {
"stream": {
"token": "sdfsdfsdfsdfsdf3ew234235wtetgdsfgew3534tg",
"url": url,
"offsetInMilliseconds": 0
}
}
}
],
"shouldEndSession": True
}
}
When I run the test in dev portal. I get a response as I should but its missing the directives.
{
"version": "1.01",
"response": {
"shouldEndSession": true
},
"sessionAttributes": {}
}
Alexa just says "There was a problem with the requested skills response."
Well I think its because the directives arent making it over. But I've tested the stream, it works. It's https. Theres a token. What am I missing?
That response from Alexa means that the skill has returned an invalid response that Alexa doesn't know how to parse.
If you haven't already, you should check your CloudWatch logs for the Lambda function to see if any errors are arising there: https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#
To the best of my knowledge, the developer portal still doesn't display directives, so you don't want to test there. From the developer portal Alexa skill test page:
Note: Service Simulator does not currently support testing audio
player directives and customer account linking.
What you can do to debug further if no errors found in CloudWatch is copy/paste the Service Request from that page and use it as a custom test for your Lambda function. On the Lambda page, click the Actions drop down and select Configure Text Event and paste your request from the developer portal into that. That'll give you a better picture of the response you're returning to Alexa. If you can't figure this out, add that response here and we'll try to puzzle things out a bit more.