EDIT 2: The following schema (provided by a colleague) works. I removed the quotation marks from the schema in the examples from Microsoft, but that still didn't work. I'm not sure what the issue is. I leave the question open in case someone else wants to provide an answer, but I've got it working.
const card = {
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
type: 'AdaptiveCard',
version: '1.0',
{
type: 'Input.Text',
placeholder: 'Name',
style: 'text',
maxLength: 50,
id: 'defaultInput'
},
actions: [
{
type: 'Action.Submit',
title: 'Siguiente',
data: {} // will be populated with form input values
}
]
}
};
I'm trying to make a form in my MS Bot using Adaptive Cards. I took the sample form from the MS site (https://blog.botframework.com/2019/07/02/using-adaptive-cards-with-the-microsoft-bot-framework/) but get the following error
The error seems to be thinking that my action type is Action.openUrl but I don't see that in my code, which is below. Any help much appreciated. Using Microsoft Bot Framework 3, Node 12.13.0.
function askPolicyNumber(session) {
const card = {
'$schema': 'https://adaptivecards.io/schemas/adaptive-card.json',
'type': 'AdaptiveCard',
'version': '1.1',
'body': [
{
'type': 'Input.Text',
'id': 'id_text'
},
{
'type': 'Input.Number',
'id': 'id_number'
}
],
'actions': [
{
'type': 'Action.messageBack',
'title': 'Submit',
'data': {
'prop1': true,
'prop2': []
}
}
]
};
const msg = new builder.Message(session).attachments([card]);
return session.send(msg);
}
EDIT:
It seems that no matter what I set the action to it keeps thinking it's an openUrl action. In fact, if I set it to openUrl and give it a url property, it works fine.
I looked at this page -- https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-actions#adaptive-cards-actions -- and followed the instructions there for 'Adaptive Cards with messageBack action', but it didn't change anything
"actions": [
{
"type": "Action.Submit",
"title": "Click me for messageBack",
"data": {
"msteams": {
"type": "messageBack",
"displayText": "I clicked this button",
"text": "text to bots",
"value": "{\"bfKey\": \"bfVal\", \"conflictKey\": \"from value\"}"
}
}
}
]
}
There are a lot of problems with what you're doing. It is recommended that everyone use Bot Builder v4 instead of v3. The main problem that your colleague solved was that you were trying to use an Adaptive Card object as though it was an Attachment object.
The blog post you linked to explains that Adaptive Cards must follow the Adaptive Cards schema. There is no Action.messageBack in the Adaptive Cards schema. Please continue referring to the documentation for more information.
Related
I am currently working on a React.js full stack application with Express back-end. I had a question regarding a design decision for the API calls. I have 3 APIs at the moment
GET /airports/
{
"total_count":269,
"items":[
{
"airport_code":"ABJ",
"city":"ABJ",
"country":"CI",
"name":"Port Bouet Airport",
"city_name":"Abidjan",
"country_name":"Cote d'Ivoire",
"lat":5.261390209,
"lon":-3.926290035,
"alt":21,
"utc_offset":0.0
},
{
"airport_code":"ABV",
"city":"ABV",
"country":"NG",
"name":"Nnamdi Azikiwe International Airport",
"city_name":"Abuja",
"country_name":"Nigeria",
"lat":9.006790161,
"lon":7.263169765,
"alt":1123,
"utc_offset":1.0
},
........
]
}
GET /airports/{airport_code}
GET /flights/
{
"total_count": 898,
"items": [
{
"flight_number": "ZG6304",
"aircraft_registration": "ZGAJG",
"departure_airport": "BAH",
"arrival_airport": "LHR",
"scheduled_departure_time": "2020-01-01T20:50:00",
"scheduled_takeoff_time": "2020-01-01T21:00:00",
"scheduled_landing_time": "2020-01-02T03:00:00",
"scheduled_arrival_time": "2020-01-02T03:10:00"
},
{
"flight_number": "ZG6311",
"aircraft_registration": "ZGAJH",
"departure_airport": "CDG",
"arrival_airport": "FRA",
"scheduled_departure_time": "2020-01-01T06:45:00",
"scheduled_takeoff_time": "2020-01-01T06:55:00",
"scheduled_landing_time": "2020-01-01T07:50:00",
"scheduled_arrival_time": "2020-01-01T08:00:00"
},
........
]
}
I am working on building an airport arrivals and departures web application using the above data. My idea was to try and combine the data of /fligts/ and /airports/ API call based on departure_airport and arrival_airport to be able to have more information inside a single array such as information about the city_name, lat, long etc. to visualize the data. I wanted to know a good approach for solving this issue keeping in mind the computational overhead of filtering and merging large sets of data. I looked into using RxJS but I have not worked with it before to be sure if it would provide a good solution
I recommend to convert the airports array to an object. After you can access the airports by keys.
const airports = {
total_count: 269,
items: [
{
airport_code: 'ABJ',
city: 'ABJ',
country: 'CI',
name: 'Port Bouet Airport',
city_name: 'Abidjan',
country_name: "Cote d'Ivoire",
lat: 5.261390209,
lon: -3.926290035,
alt: 21,
utc_offset: 0.0,
},
{
airport_code: 'ABV',
city: 'ABV',
country: 'NG',
name: 'Nnamdi Azikiwe International Airport',
city_name: 'Abuja',
country_name: 'Nigeria',
lat: 9.006790161,
lon: 7.263169765,
alt: 1123,
utc_offset: 1.0,
},
],
};
const mappedAirports = airports.items.reduce(
(result, airport) =>
(result = { ...result, [airport.airport_code]: airport }),
{}
);
console.log(mappedAirports);
Output:
{"ABJ":{"airport_code":"ABJ","city":"ABJ","country":"CI","name":"Port Bouet Airport","city_name":"Abidjan","country_name":"Cote d'Ivoire","lat":5.261390209,"lon":-3.926290035,"alt":21,"utc_offset":0},"ABV":{"airport_code":"ABV","city":"ABV","country":"NG","name":"Nnamdi Azikiwe International Airport","city_name":"Abuja","country_name":"Nigeria","lat":9.006790161,"lon":7.263169765,"alt":1123,"utc_offset":1}}
I am trying to filter my GA api V4 in python by removing pageviews under 200 in my request...
surprisingly, as i follow exactly the syntax of the documentation, i get an error message :
> "Invalid JSON payload received. Unknown name "comparisonValue" at 'report_req
uests[0].metric_filter_clauses[0].filters[0]': Proto field is not repeating, cannot start list.". Details: "[{'
#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'report_requests[0].metric_f
ilter_clauses[0].filters[0]', 'description': 'Invalid JSON payload received. Unknown name "comparisonValue" at
\'report_requests[0].metric_filter_clauses[0].filters[0]\': Proto field is not repeating, cannot start list.'}]
}]"
obviously there is a problem in my coding, but I cannot understand what it is : it is a copy paste of the documentation in https://developers.google.com/analytics/devguides/reporting/core/v4/basics#filtering...
is there a difference between the JSON POST from documentation and the one in the batch get from python?
here is the part where I build my request the problematic filter is at the end...:
return analytics.reports().batchGet(
body={
'reportRequests': [
{
'viewId': VIEW_ID,
'dateRanges': [{'startDate': '2021-07-20', 'endDate': '2021-07-27'}],
'metrics': [
{
'expression': 'ga:sessions',
'expression': 'ga:pageviews'
}],
'dimensions': [
{
'name': 'ga:pagePath'
}],
'orderBys': [
{
'orderType': 'VALUE',
'sortOrder': "DESCENDING",
'fieldName': 'ga:pageviews'
}],
'dimensionFilterClauses': [
{
'filters': [
{
'dimensionName':'ga:country',
'operator':'BEGINS_WITH',
'expressions':['fra']
}],
}],
'metricFilterClauses': [
{
'filters': [
{
'metricName':'ga:pageviews',
'operator':'GREATER_THAN',
'comparisonValue':['200']
}]
}]
}]
}
).execute()
any idea why it don't work?
thanks :)
Jacques,
comparisonValue field value should be a string, not a list, e.g. '200' instead of ['200'].
A good way to eliminate possible coding issues is to test your query directly using the Google Analytics API Explorer.
Whenever the user invokes my agent then it shows a list of options to select and also a simple response but the agent first speaks the simple response and then shows the list
Actual
user: Ok google talk to my test app.
bot: Welcome to my test app, Here's the list of options to select. (WELCOME MESSAGE)
Please select your preference (RESPONSE)
<list appears> (LIST)
Expected
user: Ok google talk to my test app.
bot: Welcome to my test app, Here's the list of options to select. (WELCOME MESSAGE)
<list appears> (LIST)
Please select your preference. (RESPONSE)
Is it possible that the assistant first speaks the welcome message,shows the list and then speaks out the response after a certain delay?
No, showing the bubble after the list is not possible.
When you add a list to your response, the spoken text will always appear before the list. This is mainly due to the fact that the spoken/chat part of the conversation is separate from the visual part of your conversation. Even when adding the response after the list in your code, the displaying of rich response is controlled by Google.
Example:
conv.ask('This is a list example.');
// Create a list
conv.ask(new List({
title: 'List Title',
items: {
'SELECTION_KEY_ONE': {
synonyms: [
'synonym 1',
'synonym 2',
'synonym 3',
],
title: 'Title of First List Item',
description: 'This is a description of a list item.',
image: new Image({
url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
alt: 'Image alternate text',
}),
},
'SELECTION_KEY_TWO': {
synonyms: [
'synonym 4',
'synonym 5',
'synonym 6',
],
title: 'Title of Second List Item',
description: 'This is a description of a list item.',
image: new Image({
url: 'https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png',
alt: 'Image alternate text',
}),
}
}
}));
conv.ask("Please make your selection");
By the look of your example it seems as if you are trying to show the user a couple options on the screen to control the conversation, are you sure Suggestion Chips wouldn't be a better fit for this? These chips are intended to give the user options and are far easier to implement than a list.
Delaying the speech, not the bubble
If you don't want to go that way, what you could do, is add an delay in the spoken text via SSML, but this would only change the experience for people using your action via voice. It wouldn't change the display location of the speech bubble when using the Google Assistant on your phone. For anyone using your action without a screen, this could cause confusion because the speech is being delayed for a list, which is never going to show on their device since it has no screen.
Design in a voice first experience
In general it is a good practice to design your conversation around the voice only part of your conversation. By making your conversation dependable on a list, you limit the amount of platforms you can deploy your action to. A voice first approach to this problem could be to create intents for each option your action supports and opening your welcome intent with a generic message such as "How can I assist you?" and having a fallback intent in which you assist the user by speaking out the different options that they can use. This could be combined with Suggestion Chips to still give the guiding visuals that you desire.
It is a bit more work to implement, but it does give your bot a great more amount of flexibility in its conversation and the amount of platforms it can support.
Add webhook to your action and use the Browsing Carousel JSON for the intent. Add simpleReponse node after the list items to add a response after list is displayed. Sample JSON for Browsing Carousel:
{
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Here's an example of a browsing carousel."
}
},
{
"carouselBrowse": {
"items": [
{
"title": "Title of item 1",
"openUrlAction": {
"url": "https://example.com"
},
"description": "Description of item 1",
"footer": "Item 1 footer",
"image": {
"url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
"accessibilityText": "Image alternate text"
}
},
{
"title": "Title of item 2",
"openUrlAction": {
"url": "https://example.com"
},
"description": "Description of item 2",
"footer": "Item 2 footer",
"image": {
"url": "https://storage.googleapis.com/actionsresources/logo_assistant_2x_64dp.png",
"accessibilityText": "Image alternate text"
}
}
]
}
}
]
}
}
}
}
Refer to https://developers.google.com/assistant/conversational/rich-responses#df-json-basic-card
Using node-recurly, I can create a subscription object and pass it to recurly.subscriptions.create call:
const subscription = {
plan_code: plan.code,
currency: 'USD',
account: {
account_code: activationCode,
first_name: billingInfo.first_name,
last_name: billingInfo.last_name,
email: billingInfo.email,
billing_info: {
token_id: paymentToken,
},
},
};
I would also like to add subscription_add_ons property, which, looking at the documentation, supposed to be an array of add-ons. I tried passing it like this:
subscription_add_ons: [
{
add_on_code: shippingMethod.servicelevel_token,
unit_amount_in_cents: parseFloat(shippingMethod.amount) * 100,
},
],
The server returned an error:
Tag <subscription_add_ons> must consist only of sub-tags named
<subscription_add_on>
I attempted this:
subscription_add_ons: [
{
subscription_add_on: {
add_on_code: shippingMethod.servicelevel_token,
unit_amount_in_cents: parseFloat(shippingMethod.amount) * 100,
},
},
],
Got back this error:
What's the proper format to pass subscription add on in this scenario?
The proper format is:
subscription_add_ons: {
subscription_add_on: [{
add_on_code: shippingMethod.servicelevel_token,
unit_amount_in_cents: parseFloat(shippingMethod.amount) * 100,
}],
},
I ended up doing this which works whether you have 1 add-on or multiple add-ons. subscription_add_ons is an array which can contain 1 or more subscription add ons. I then send over the details (along with other info) in the subscription update call. This is similar to what you attempted in your original post so I'm not sure why that didn't work for you.
details.subscription_add_ons = [
{ subscription_add_on: {add_on_code: "stream", quantity: 3} },
{ subscription_add_on: {add_on_code: "hold", quantity: 2} }
];
update !important: The API has changed a lot, this question shouldn't be taken into consideration anymore
I am trying to use the REST api (via Node.js API) to create cards that the user can respond to and create an interaction in this way.
Reading the docs the creator attribute is not really specified anywhere, so I have no idea how to insert that.
Also this video doesn't help. Nor this guide =)
I believe there is an URL I should set as callback somehow? I'd like to know how to get these responses, please.
update
This is the card I am sending.
{
bundleId: 'veryuniqueBundle',
id: 'veryuniqueBundle:reply',
text: "want to hear moar?",
menuItems: [
{action: "REPLY"}
]
}
that's the response I get:
{
"collection": "timeline",
"itemId": "119c4dc8-c0ce-4a83-aa76-41aab4e8dbe1",
"operation": "INSERT",
"verifyToken": "42",
"userToken": "id:520ef63cde31145deb000001",
"userActions": [
{
"type": "REPLY"
}
]
}
The problem is, I can't see what the user responded (an text) and the reference to the original card id (or bundle) that was responded to. How can I get those
Cards do not provide a direct callback. Instead, when a user selects a menu item it causes the card to be updated with their menu selection. This change subsequently triggers a notification ping to your timeline subscription.
Follow these steps to detect a menu item selection:
Subscribe to notifications for changes in the timeline collection
{
"collection": "timeline",
"userToken": "awesome_kitty",
"verifyToken": "random_hash_to_verify_referer",
}
Insert a timeline card with a custom menu item
{
"text": "Hello world",
"menuItems": [
{
"action": "CUSTOM",
"id": "complete"
"values": [{
"displayName": "Complete",
"iconUrl": "http://example.com/icons/complete.png"
}]
}
]
}
Select the item on Glass
Receive the notification on your subscription URL
{
"collection": "timeline",
"itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
"operation": "UPDATE",
"userToken": "harold_penguin",
"userActions": [
{
"type": "CUSTOM",
"payload": "PING"
}
]
}
Do cool stuff in your code
???
Profit