Created
December 9, 2016 23:57
-
-
Save LosantGists/d423e2bdc10d3f3014b32d97371c322a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"globals":[ | |
{ | |
"key":"onMessageFinishedUrl", | |
"type":"string" | |
}, | |
{ | |
"key":"doorResponseUrl", | |
"type":"string" | |
}, | |
{ | |
"key":"openDoorCode", | |
"type":"string" | |
}, | |
{ | |
"key":"slackUrl", | |
"type":"string" | |
}, | |
{ | |
"key":"slackToken", | |
"type":"string" | |
} | |
], | |
"triggers":[ | |
{ | |
"type":"webhook", | |
"meta":{ | |
"category":"trigger", | |
"name":"webhook", | |
"label":"On Call Received", | |
"x":120, | |
"y":120, | |
"uiId":"r1wvM6J7l", | |
"description":"Webhook is called by Twilio when our number is dialed. This URL must be set in Twilio." | |
}, | |
"config":{ | |
}, | |
"key":"5842423fc0b30a010006496e", | |
"outputIds":[ | |
[ | |
"HynOzakmg" | |
] | |
] | |
}, | |
{ | |
"type":"webhook", | |
"meta":{ | |
"category":"trigger", | |
"name":"webhook", | |
"label":"On Message Finished", | |
"x":380, | |
"y":120, | |
"uiId":"BypLF6JXe", | |
"description":"" | |
}, | |
"config":{ | |
}, | |
"key":"584250c5ad4390010068a937", | |
"outputIds":[ | |
[ | |
"HkTKKak7l" | |
] | |
] | |
}, | |
{ | |
"type":"webhook", | |
"meta":{ | |
"category":"trigger", | |
"name":"webhook", | |
"label":"Door Action", | |
"x":1140, | |
"y":120, | |
"uiId":"SJfoLCJXg", | |
"description":"When somebody types a slash command in Slack, this is the webhook they'll hit." | |
}, | |
"config":{ | |
}, | |
"key":"5842568dad4390010068a939", | |
"outputIds":[ | |
[ | |
"HkxtrDf7x" | |
] | |
] | |
}, | |
{ | |
"type":"webhook", | |
"meta":{ | |
"category":"trigger", | |
"name":"webhook", | |
"label":"Door Response", | |
"x":640, | |
"y":120, | |
"uiId":"BkFcCreQe", | |
"description":"This webhook is called after the user leaves their message at the door. Our team's response will ultimately go through here." | |
}, | |
"config":{ | |
}, | |
"key":"5842ce6ec0b30a0100064973", | |
"outputIds":[ | |
[ | |
"HyHjCreXg" | |
] | |
] | |
} | |
], | |
"nodes":[ | |
{ | |
"type":"DebugNode", | |
"meta":{ | |
"category":"output", | |
"name":"debug", | |
"label":"On Call Placed", | |
"x":120, | |
"y":280, | |
"description":"" | |
}, | |
"config":{ | |
"message":"On Call Placed", | |
"property":"" | |
}, | |
"id":"SyWOGpy7x", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Webhook Reply", | |
"x":120, | |
"y":200, | |
"description":"This is the message the user first hears in the callbox. When they finish their recording, they are redirected to a different Losant webhook." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":" text/xml" | |
} | |
], | |
"replyIdPath":"", | |
"responseCodeTemplate":"", | |
"bodyTemplate":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Say voice=\"woman\">Welcome. At the tone, please state your name and whom you are here to see, then press any key to continue.</Say>\n<Record action=\"{{globals.onMessageFinishedUrl}}\" playBeep=\"true\" maxLength=\"10\" />\n</Response>", | |
"bodyTemplateType":"string" | |
}, | |
"id":"HynOzakmg", | |
"outputIds":[ | |
[ | |
"SyWOGpy7x" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Webhook Reply", | |
"x":380, | |
"y":200, | |
"description":"This is what the user hears after entering their message. We redirect the Twilio response to a third Losant webhook, which will handle our Slack response." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":" text/xml" | |
} | |
], | |
"replyIdPath":"", | |
"responseCodeTemplate":"", | |
"bodyTemplate":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Say voice=\"woman\">Thank you. Please wait.</Say>\n<Redirect>{{globals.doorResponseUrl}}</Redirect>\n</Response>", | |
"bodyTemplateType":"string" | |
}, | |
"id":"HkTKKak7l", | |
"outputIds":[ | |
[ | |
"ByO2sJlmx" | |
] | |
] | |
}, | |
{ | |
"type":"GetValueNode", | |
"meta":{ | |
"category":"data", | |
"name":"get-value", | |
"label":"Get webhookId", | |
"x":1140, | |
"y":280, | |
"description":"" | |
}, | |
"config":{ | |
"keyName":"webhookId", | |
"valuePath":"data.webhookId", | |
"defaultValueType":"json", | |
"defaultValue":"null" | |
}, | |
"id":"r1bZEkeQg", | |
"outputIds":[ | |
[ | |
"B1GQr1l7g" | |
] | |
] | |
}, | |
{ | |
"type":"DebugNode", | |
"meta":{ | |
"category":"output", | |
"name":"debug", | |
"label":"On Message Finished", | |
"x":380, | |
"y":360, | |
"description":"" | |
}, | |
"config":{ | |
"message":"On Message Finished", | |
"property":"" | |
}, | |
"id":"HJqlrylme", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"ConditionalNode", | |
"meta":{ | |
"category":"logic", | |
"name":"conditional", | |
"label":"Webhook ID?", | |
"x":1140, | |
"y":360, | |
"description":"Is somebody at the door right now? If somebody already answered this user, or the request timed out, the answer is no." | |
}, | |
"config":{ | |
"expression":"!!{{data.webhookId}}" | |
}, | |
"id":"B1GQr1l7g", | |
"outputIds":[ | |
[ | |
"H1asbufmg" | |
], | |
[ | |
"By7o_PMXx" | |
] | |
] | |
}, | |
{ | |
"type":"HttpNode", | |
"meta":{ | |
"category":"output", | |
"name":"http", | |
"label":"Send to Slack", | |
"x":380, | |
"y":280, | |
"description":"Call our Slack webhook (defined in Slack), which alerts our chosen channel that somebody is at the door." | |
}, | |
"config":{ | |
"method":"POST", | |
"uriTemplate":"{{globals.slackUrl}}", | |
"responsePath":"", | |
"headerInfo":[ | |
], | |
"bodyTemplate":"{\n \"text\": \"<!here> *Somebody is at the front door! <{{data.body.RecordingUrl}}|(Hear Message)>*\\n\\nType `/door [OPEN|WAIT] [customMessage]` to respond and optionally open the door.\"\n}" | |
}, | |
"id":"ByO2sJlmx", | |
"outputIds":[ | |
[ | |
"HJqlrylme" | |
] | |
] | |
}, | |
{ | |
"type":"StoreValueNode", | |
"meta":{ | |
"category":"data", | |
"name":"store-value", | |
"label":"Store webhookId", | |
"x":640, | |
"y":200, | |
"description":"We'll need this value because we're responding to this webhook in a different branch of our workflow." | |
}, | |
"config":{ | |
"keyName":"webhookId", | |
"valueType":"template", | |
"value":"{{data.replyId}}" | |
}, | |
"id":"HyHjCreXg", | |
"outputIds":[ | |
[ | |
"SyQdkhl7x" | |
] | |
] | |
}, | |
{ | |
"type":"DelayNode", | |
"meta":{ | |
"category":"logic", | |
"name":"delay", | |
"label":"Timeout 12s", | |
"x":640, | |
"y":280, | |
"description":"Twilio times out after 15 seconds. Let's set this for 12 seconds just to be safe." | |
}, | |
"config":{ | |
"delay":12 | |
}, | |
"id":"SyQdkhl7x", | |
"outputIds":[ | |
[ | |
"SkSWfvOXx" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Dismiss / Timeout", | |
"x":740, | |
"y":600, | |
"description":"Nobody is answering. Let's reply to the webhook request letting the user nobody is in the office." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":"text/xml" | |
} | |
], | |
"replyIdPath":"data.webhookId", | |
"responseCodeTemplate":"", | |
"bodyTemplate":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Say voice=\"woman\">Sorry, but nobody is answering in the office at this time. Please contact your expecting party directly or try again. Goodbye.</Say>\n</Response>", | |
"bodyTemplateType":"string" | |
}, | |
"id":"SJgex2e7x", | |
"outputIds":[ | |
[ | |
"H1iJYDOQg", | |
"SJFkiD_7e" | |
] | |
] | |
}, | |
{ | |
"type":"DebugNode", | |
"meta":{ | |
"category":"output", | |
"name":"debug", | |
"label":"On Door Action", | |
"x":1140, | |
"y":600, | |
"description":"" | |
}, | |
"config":{ | |
"message":"On Door Action", | |
"property":"" | |
}, | |
"id":"ByeH9MnxQl", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"ConditionalNode", | |
"meta":{ | |
"category":"logic", | |
"name":"conditional", | |
"label":"Redirected yet?", | |
"x":640, | |
"y":520, | |
"description":"Has this user already been redirected once?" | |
}, | |
"config":{ | |
"expression":"{{data.query.rdrcount}} === '1'" | |
}, | |
"id":"rJpfWWfml", | |
"outputIds":[ | |
[ | |
"r1H0bbzmx" | |
], | |
[ | |
"SJgex2e7x" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Redirect once", | |
"x":540, | |
"y":600, | |
"description":"To buy ourselves more time, let's redirect this webhook right back to itself." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":" text/xml" | |
} | |
], | |
"replyIdPath":"", | |
"responseCodeTemplate":"", | |
"bodyTemplate":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n<Redirect>{{globals.doorResponseUrl}}?rdrcount=1</Redirect>\n</Response>", | |
"bodyTemplateType":"string" | |
}, | |
"id":"r1H0bbzmx", | |
"outputIds":[ | |
[ | |
"BkLFmOMXl" | |
] | |
] | |
}, | |
{ | |
"type":"ConditionalNode", | |
"meta":{ | |
"category":"logic", | |
"name":"conditional", | |
"label":"Verify", | |
"x":1140, | |
"y":200, | |
"description":"Make sure this request came from our Slack account." | |
}, | |
"config":{ | |
"expression":"{{data.body.token}} === {{globals.slackToken}}" | |
}, | |
"id":"HkxtrDf7x", | |
"outputIds":[ | |
[ | |
], | |
[ | |
"r1bZEkeQg" | |
] | |
] | |
}, | |
{ | |
"type":"RawFunctionNode", | |
"meta":{ | |
"category":"logic", | |
"name":"function", | |
"label":"Parse message", | |
"x":1240, | |
"y":440, | |
"description":"Here we make sense of the body of the slash command – whether we should open the door and what should be said to the user through the callbox." | |
}, | |
"config":{ | |
"script":"if(payload.data && payload.data.body && payload.data.body.text) {\n if(payload.data.body.text.indexOf('WAIT') === 0) {\n // tell the user to wait there (no message)\n payload.replyMsg = 'Thank you. Please wait where you are. Somebody is on their way down to meet you.';\n } else if(payload.data.body.text.indexOf('OPEN') === 0) {\n // open the door ...\n payload.openDoor = true;\n payload.replyMsg = payload.data.body.text.replace('OPEN','');\n if(payload.replyMsg.trim() === '') {\n // if no message was included, add this one on.\n payload.replyMsg = 'Thank you. The door is opening now. Please head up to the 3rd floor.';\n }\n } else {\n // speak the message. do not open the door. end the call.\n payload.replyMsg = payload.data.body.text;\n }\n}" | |
}, | |
"id":"By7o_PMXx", | |
"outputIds":[ | |
[ | |
"HJpHzuf7g", | |
"ByxAoDzmg" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Reply to door", | |
"x":1340, | |
"y":520, | |
"description":"Answer the Door Response webhook. If we're opening the door, play the touchtones to open the door after speaking." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":"text/xml" | |
} | |
], | |
"replyIdPath":"data.webhookId", | |
"responseCodeTemplate":"", | |
"bodyTemplate":"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Say voice=\"woman\">{{replyMsg}}</Say>\n{{#eq openDoor true}}<Play digits=\"{{globals.openDoorCode}}\" />{{/eq}}\n</Response>", | |
"bodyTemplateType":"string" | |
}, | |
"id":"ByxAoDzmg", | |
"outputIds":[ | |
[ | |
"H1iJYDOQg" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Slack reply (nobody at door)", | |
"x":940, | |
"y":520, | |
"description":"Tell this user privately in Slack that nobody is at the door right now." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":"application/json" | |
} | |
], | |
"replyIdPath":"", | |
"responseCodeTemplate":"200", | |
"bodyTemplate":"{\n \"text\": \"Nobody is at the door right now.\"\n}", | |
"bodyTemplateType":"string" | |
}, | |
"id":"H1asbufmg", | |
"outputIds":[ | |
[ | |
"ByeH9MnxQl" | |
] | |
] | |
}, | |
{ | |
"type":"WebhookReplyNode", | |
"meta":{ | |
"category":"output", | |
"name":"webhook-reply", | |
"label":"Slack reply (bingo)", | |
"x":1140, | |
"y":520, | |
"description":"Tell everybody in Slack who replied to the door request and what they said." | |
}, | |
"config":{ | |
"headerInfo":[ | |
{ | |
"keyTemplate":"Content-type", | |
"valueTemplate":"application/json" | |
} | |
], | |
"replyIdPath":"", | |
"responseCodeTemplate":"200", | |
"bodyTemplate":"{\n \"text\": \"User <@{{data.body.user_id}}|{{data.body.user_name}}> replied with ...\\n\\n{{data.body.text}}\",\n\"response_type\":\"in_channel\"\n}", | |
"bodyTemplateType":"string" | |
}, | |
"id":"HJpHzuf7g", | |
"outputIds":[ | |
[ | |
"ByeH9MnxQl" | |
] | |
] | |
}, | |
{ | |
"type":"DebugNode", | |
"meta":{ | |
"category":"output", | |
"name":"debug", | |
"label":"On Redirect", | |
"x":540, | |
"y":680, | |
"description":"" | |
}, | |
"config":{ | |
"message":"On Redirect", | |
"property":"" | |
}, | |
"id":"BkLFmOMXl", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"GetValueNode", | |
"meta":{ | |
"category":"data", | |
"name":"get-value", | |
"label":"Get webhookId", | |
"x":640, | |
"y":360, | |
"description":"Let's make sure the webhookId still exists before continuing." | |
}, | |
"config":{ | |
"keyName":"webhookId", | |
"valuePath":"data.webhookId", | |
"defaultValueType":"json", | |
"defaultValue":"null" | |
}, | |
"id":"SkSWfvOXx", | |
"outputIds":[ | |
[ | |
"ry_BGPu7x" | |
] | |
] | |
}, | |
{ | |
"type":"ConditionalNode", | |
"meta":{ | |
"category":"logic", | |
"name":"conditional", | |
"label":"Webhook ID?", | |
"x":640, | |
"y":440, | |
"description":"Has this webhook been answered yet? The presence of a webhook ID tells us that is has not." | |
}, | |
"config":{ | |
"expression":"!!{{data.webhookId}}" | |
}, | |
"id":"ry_BGPu7x", | |
"outputIds":[ | |
[ | |
], | |
[ | |
"rJpfWWfml" | |
] | |
] | |
}, | |
{ | |
"type":"StoreValueNode", | |
"meta":{ | |
"category":"data", | |
"name":"store-value", | |
"label":"Clear webhookId", | |
"x":1140, | |
"y":760, | |
"description":"Clear the webhookId from storage, which prevents any further action on the door or on Slack." | |
}, | |
"config":{ | |
"keyName":"webhookId", | |
"valueType":"clear", | |
"value":"" | |
}, | |
"id":"H1iJYDOQg", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"DebugNode", | |
"meta":{ | |
"category":"output", | |
"name":"debug", | |
"label":"On Timeout", | |
"x":740, | |
"y":760, | |
"description":"" | |
}, | |
"config":{ | |
"message":"On Timeout", | |
"property":"" | |
}, | |
"id":"H1C85D_Xx", | |
"outputIds":[ | |
] | |
}, | |
{ | |
"type":"HttpNode", | |
"meta":{ | |
"category":"output", | |
"name":"http", | |
"label":"Timeout to Slack", | |
"x":740, | |
"y":680, | |
"description":"Tell the Slack channel that we failed to respond in time." | |
}, | |
"config":{ | |
"method":"POST", | |
"uriTemplate":"{{globals.slackUrl}}", | |
"responsePath":"", | |
"headerInfo":[ | |
], | |
"bodyTemplate":"{\n \"text\": \"Nobody answered in time. The person at the door has been told that nobody is home.\"\n}" | |
}, | |
"id":"SJFkiD_7e", | |
"outputIds":[ | |
[ | |
"H1C85D_Xx" | |
] | |
] | |
} | |
], | |
"name":"Front Door", | |
"enabled":true, | |
"description":"Clear the request", | |
"applicationId":"5842419f37c9d60100e16386", | |
"_type":"flow", | |
"_exportDate":"2016-12-09T23:55:49.963Z" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment