new validation webhook feature inside Twilio Collect action - twilio

I saw that Twilio added a new feature when using the Collect action, which allows one to validate the user answer by means of a web hook. Here's the link: https://www.twilio.com/docs/autopilot/actions/collect
I'm wondering if this is already part of the framework or not. I've tried small examples, and also the long sample they have in the documentation (using my own webhooks of course), and the validation web hook is not being triggered, or called at all.
as an example I have the following:
{"actions": [
{
"collect": {
"name": "date_questions",
"questions": [
{
"question": "What date?",
"name": "date",
"type": "Twilio.DATE",
"validate": {
"on_failure": {
"messages": [
{
"say": "Sorry, that's not a valid date."
},
{
"say": "Hmm, I'm not understanding what you're saying"
}
],
"repeat_question": true
},
"webhook": {
"method": "POST",
"url": "https://my_validation_wh"
}
}
}
],
"on_complete": {
"redirect": "https://my_on_complete_wh"
}
}
}
]}

Related

Add Complex Logic To A Twilio Whatsapp Bot

I am developing a Whatsapp Bot in Twilio to collect survey data Covid-19 related. However, i want to add complex logic in my program task. For example, i only want to display a question asking if the user is pregnant or not if the user has selected female as her gender. Plus i want to add a range of the questions asking weight and height.
Below is the sample code for my task
"actions": [
{
"say": "Thank you for making the time for this survey. Your answers help us improve the service!"
},
{
"collect": {
"name": "csat_answers",
"questions": [
{
"question": "Have you completed this survey before??",
"name": "first_time",
"type": "Twilio.YES_NO"
},
{
"question": "Please enter your username",
"name": "usernmae"
},
{
"question": "How old are you?",
"name": "age",
"type": "Twilio.NUMBER"
},
{
"question": "What Gender are you",
"name": "Gender"
},
{
"question": "Are you pregnant?",
"name": "pregnant",
"type": "Twilio.YES_NO"
},
{
"question": "What is your height (cm)?",
"name": "height",
"type": "Twilio.NUMBER"
},
{
"question": "What is your weight (kg)?",
"name": "weight",
"type": "Twilio.NUMBER"
}
],
"on_complete": {
"redirect": {
"method": "POST",
"uri": "task://complete_survey"
}
}
}
}
]
}```
You will need to introduce logic in your Autopilot assistant, static JSON will not provide this capability.
How to Use Twilio Functions with Twilio Autopilot
If you are not a developer but a builder, you will want to look at Twilio Studio, to graphically draw out your survey flow and handle survey state.
Conduct a Survey with Twilio Studio
Twilio developer evangelist here.
There's no way to do conditional Collect flows in Autopilot so that logic would need to be a part of your app code. You can implement that conditional logic with Autopilot using Twilio Functions, our serverless environment tool for Node.js applications.
This Node.js quickstart has some of what you're looking for.
You could have one Autopilot task which redirects to the Function which would check what the user said. That Redirect task could include this JSON:
{
"actions": [
{
"collect": {
"name": "pregnant_collect",
"questions": [
{
"question": "Are you pregnant?",
"name": "pregnant",
"type": "Twilio.YES_NO"
}
],
"on_complete": {
"redirect": {
"method": "POST",
"uri": "https://YOUR-TWILIO-FUNCTION.twil.io/Function_Name"
}
}
}
}
]
}
and then use a conditional in the Twilio Function that above Task redirects to (above, it's https://YOUR-TWILIO-FUNCTION.twil.io/Function_Name) to check what the user said. The Twilio Autopilot task could include this JSON:
exports.handler = function(context, event, callback) {
//Memory from the answered question
let memory = JSON.parse(event.Memory);
//array of "actions" objects
let actions = [];
//response item to return/add to responseObject in Actions
let responseItem;
//get answer from Memory
let pregnant_answer = Memory.twilio.collected_data.pregnant_collect.answers.pregnant.answer;
//Yes or No
if (pregnant_answer === "Yes") {
responseItem = {
"say": "WHATEVER YOU WANT TO SAY FOR YES"
};
actions.push(responseItem);
}
else {
responseItem = {
"say": "WHATEVER YOU WANT TO SAY FOR NO"
};
actions.push(responseItem);
}
responseItem = {
"redirect": {
"method": "POST",
"uri": "task://next_task"
}
};
actions.push(responseItem);
let responseObject = {
"actions": actions
};
callback(null, responseObject);
};
Let me know if this helps at all! <3

Slack API invalid_block

I'm building a simple slack bot and I am playing with the checkboxes element.
When I return the following from my API in a JSON response to a slash-command I get an error failed with the error "invalid_blocks", however, when I put this in the block-kit-builder it works perfectly (including "sending to slack" button)
Any ideas why this is failing when I run my slash command - and is it possible to see more detailed error messages from slack?
{
"blocks": [
{
"elements": [
{
"style": "primary",
"text": {
"emoji": true,
"text": "Create new TODO list",
"type": "plain_text"
},
"type": "button",
"value": "value"
},
{
"style": "primary",
"text": {
"emoji": true,
"text": "Help",
"type": "plain_text"
},
"type": "button",
"value": "value"
}
],
"type": "actions"
},
{
"text": {
"text": "Today",
"type": "mrkdwn"
},
"type": "section"
},
{
"elements": [
{
"initial_options": [
{
"text": {
"text": "Get Into the garden",
"type": "mrkdwn"
},
"value": "foo"
}
],
"options": [
{
"text": {
"text": "Get Into the garden",
"type": "mrkdwn"
},
"value": "foo"
}
],
"type": "checkboxes"
},
{
"style": "primary",
"text": {
"emoji": true,
"text": "Add new Task",
"type": "plain_text"
},
"type": "button",
"value": "value"
}
],
"type": "actions"
}
],
"type": "home"
}
I am using the Slack Web API. I was getting the similar error. After a lot of looking around, here's how I solved it.
import json
blocks = [{...}]
payload = {
"blocks": json.dumps(blocks)
}
You will then send this payload.
in the api, the "blocks" parameter need to be string type. Did you convert it to string or you use it as a JSON ?
https://api.slack.com/methods/chat.postMessage
In the Block Kit Builder, the data is a JSON with a blocks key.
In the Slack API, the blocks param is only the list of JSON objects.
blocks = [
{
"text": {
"text": "Its the list of your blocks",
"type": "mrkdwn"
},
"type": "section"
}
]
text = 'Alternative data in text'
client.chat_postMessage(channel=channel_id, blocks=blocks, text=text)
Another cause of this problem seems to be too many blocks being returned. I can't find any documentation about this whatsoever, but personal experience seems to indicate about 20 blocks is the maximum.
An alternative is to return fewer blocks, with paging actions -- paging works well with the "replace" message so that the content being paged through does not result in many separate messages.
It appears that not all valid elements in block kit tool can be posted as a message, despite the fact that message preview works fine in the Block Tool.
In my case, the code failed when I included an input block and passed when i removed it. The input block was generated by the Block Kit tool.
{
"type": "input",
"element": {
"type": "plain_text_input",
"action_id": "plain_text_input-action"
},
"label": {
"type": "plain_text",
"text": "Feedback",
"emoji": true
}
}
The error was
{'ok': False, 'error': 'invalid_blocks'}
Also, although the documentation for python says you need to urlEncode the JSON-based array, there is no example, and it is incorrect. https://api.slack.com/methods/chat.postMessage
You can see on line 29 in the SDK test code below that blocks= takes a regular list of dicts not a string.
https://github.com/slackapi/python-slack-sdk/blob/c9dc6aa0907a72c16cf36aa15e7e80031a9fdce2/integration_tests/samples/basic_usage/sending_a_message.py

Graph Pagination in Logic Apps

I'm trying to fetch all users from a specific group via an HTTP connector, a registered app, and Microsoft Graph.
The registered app has Directory.Read.All permissions.
My idea is that I'm calling the nextLink as long as it's there while appending all of the fetched users' userPrincipalName to an array eventually filling the array with all users of the group.
My Logic App looks like this:
Unfortunately, I'm just 1 reputation short of posting images, please forgive. The 3 links should provide an overview of the structure of my app.
First, nextLink is initialized to the first Graph API endpoint. This variable is set to the current nextLink through each iteration of the until loop.
Second, For the purpose of this exercise, I only get the top 5. I know there are only 9 users:
Lastly, I call the union method on the "users" array that I initialized earlier and the "value" array from the HTTP get method, to get one single array consisting of all users:
The issue is that the HTTP action always returns the same top 5 users. I've checked that the nextLink provided in the first HTTP GET call to Graph, is correct by copying it from the Runs history and pasting it into Microsoft Graph Explorer and there the next 4 users are correctly returned.
I also made sure that, for each iteration in the until loop, I call the Graph API with the nextLink from the previous iteration as expected.
The nextLink returned inside of the Logic App is exactly the same when I test it in Graph Explorer, but the same nextLink returns 2 different results when called from Graph Explorer and inside my Logic App.
Why is the result always the same top 5 users and not the next 4 users as expected?
If not sure about the reason why you will get this issue, but based on your requirement, I did a sample below:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "GetGroupUrl",
"type": "string",
"value": "https://graph.microsoft.com/v1.0/groups/<your group id>/members?$select=userPrincipalName&$top=5"
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_2": {
"inputs": {
"variables": [
{
"name": "users",
"type": "array"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Until": {
"actions": {
"Compose": {
"inputs": "#union(variables('users'),body('HTTP')['value'])",
"runAfter": {
"HTTP": [
"Succeeded"
]
},
"type": "Compose"
},
"HTTP": {
"inputs": {
"authentication": {
"audience": "https://graph.microsoft.com",
"clientId": "<app id>",
"secret": "<app secret>",
"tenant": "<your secret>",
"type": "ActiveDirectoryOAuth"
},
"method": "GET",
"uri": "#variables('GetGroupUrl')"
},
"runAfter": {},
"type": "Http"
},
"Set_variable": {
"inputs": {
"name": "GetGroupUrl",
"value": "#{if(equals(body('HTTP')?['#odata.nextLink'], null),null,body('HTTP')['#odata.nextLink'])}"
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"expression": "#equals(variables('GetGroupUrl'), '')",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Initialize_variable_2": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"method": "GET",
"schema": {
"properties": {
"text": {
"type": "string"
}
},
"type": "object"
}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
You can just replace the params with your own and paste it into your logic app code view and test it .
It works for me, as you can see , each request results are different :
Hope it helps .
This issue solved by OP self, this issue is due to queries in request URL , copy OP's comment as an answer :
After fiddling a bit more around with what each of you providing I
found a solution. It seems that when the query arguments are passed to
the HTTP GET outside of the endpoint itself (meaning in the "queries"
field inside of the block) it seems to keep overriding the nextLink.
When writing the endpoint URL out entirely with the odata parameters,
it works as intended.

How fetch "webParts" from site pages

I am trying to fetch Pages from Sharepoint sites using graph API.
But when we make GET request with
https://graph.microsoft.com/beta/sites/{site-id}/pages/{page-id}
the response consists of webParts which only have type and data.
Inside data we have an id(which same as type) and an instanceId that is unique for every webPart.
Sample webPart:
{
"type": "d1d91016-032f-456d-98a4-721247c305e8",
"data": {
"id": "d1d91016-032f-456d-98a4-721247c305e8",
"instanceId": "c54a74ef-86c1-44aa-9ba4-802e6841e3a7"
}
My goal is to fetch webPages with complete details and then backup them to a local drive in any format.
The documentation of graph API shows that the responce would consist of complete details for the webPart, but it is not so.
Documentation link: https://learn.microsoft.com/en-us/graph/api/sitepage-get?view=graph-rest-beta&tabs=http
Sample request URL:
https://graph.microsoft.com/beta/sites/m365x214355.sharepoint.com,c1e5444e-12d8-43d3-96b1-f2f66559ef58,b181bdf0-9680-4988-81f7-a24aee4afd6a/pages
Webpart repsonse:
"webParts": [
{
"type": "rte",
"data": {
"innerHTML": "<p>Take a look at the team behind delivering amazing fashion events for Contoso.</p><p>Find out how the team uses the latest technology to plan amazing fashion shows and gather customer feedback for future events.</p><p>Meet the people behind Contoso's events, learn how to plan your own event, and find the necessary resources to run highly successful fashion shows, premiers, and extravaganzas!</p>"
}
},
{
"type": "d1d91016-032f-456d-98a4-721247c305e8",
"data": {
"id": "d1d91016-032f-456d-98a4-721247c305e8",
"instanceId": "c54a74ef-86c1-44aa-9ba4-802e6841e3a7"
}
},
{
"type": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"id": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"instanceId": "75ccfeba-ad6c-416d-a859-4a6b114e156e"
}
},
{
"type": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"id": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"instanceId": "f04e02fb-45e6-4e74-9f46-0c8d90e7fb8d"
}
},
{
"type": "275c0095-a77e-4f6d-a2a0-6a7626911518",
"data": {
"id": "275c0095-a77e-4f6d-a2a0-6a7626911518",
"instanceId": "c1a222b0-624e-4e30-b544-d2a67e8e1112"
}
}
Expected Response format:
"webParts": [
{
"type": "rte",
"data": {
"innerHTML": "<p>Here are the team's upcoming events:</p>"
}
},
{
"type": "d1d91016-032f-456d-98a4-721247c305e8",
"data": {
"title": "Events",
"description": "Display upcoming events",
"serverProcessedContent": {
"htmlStrings": {},
"searchablePlainTexts": {
"title": ""
},
"imageSources": {},
"links": {
"baseUrl": "https://www.contoso.com/sites/Engineering"
},
"componentDependencies": {
"layoutComponentId": "8ac0c53c-e8d0-4e3e-87d0-7449eb0d4027"
}
},
"dataVersion": "1.0",
"properties": {
"selectedListId": "032e08ab-89b0-4d8f-bc10-73094233615c",
"selectedCategory": "",
"dateRangeOption": 0,
"startDate": "",
"endDate": "",
"isOnSeeAllPage": false,
"layoutId": "FilmStrip",
"dataProviderId": "Event",
"webId": "0764c419-1ecc-4126-ba32-0c25ae0fffe8",
"siteId": "6b4ffc7a-cfc2-4a76-903a-1cc3686dee23"
}
}
}
]
I want webParts in the format as per documentation.
If the instanceId is unique then there might be some reference table to match these instanceIds and fetch the detailed webParts structure.

Creating sections in swagger

I'm writing a swagger spec and I have three separate endpoints. How do I separate them in my documentation? I want to have a clear distinction between example: Users, Posts & Other. So each one would have a CRUD description and displayed in swagger UI it would look like:
USERS
// user specs
POST
// post specs
OTHER
// other specs
You need to use tags to accomplish this.
So, on your "paths" object, you sort all your routes and on each one, you add a "tags": ["{resource}"] where it should be grouped.
For example:
"paths": {
"/users": {
"get": {
"tags": ["User"],
"description": "...",
},
"post": {
"tags": ["User"],
"description": "...",
}
},
"/posts": {
"get": {
"tags": ["Post"],
"description": "...",
},
"post": {
"tags": ["Post"],
"description": "...",
}
},
"/other": {
"get": {
"tags": ["Other"],
"description": "...",
},
"post": {
"tags": ["Other"],
"description": "...",
}
},
}
This is not obvious at all on the documentation. Actually the documentation is very complete but lacks an index and some organisation.

Resources