Craig Burton

Logs, Links, Life and Lexicon: and Code

Craig Burton header image 2

Intro to Webhooks, Twilio and KRL Tutorial

September 15th, 2010 · No Comments · Daily Thesis, Innovation, Tutorials

go ahead and share

To experience the results of this tutorial, dial 801-895-3972 on your cell phone.

Here is Tutorial #9 in the Context Automation Tutorial Series. In this tutorial, I introduce the viewer to KRL Webhooks and I give and example on how to interface with the Twilio Cloud-based communications service.


Here is the source to the App with lots of comments.

   1: ruleset a35x58 {

   2:   meta {

   3:     name "TwilioYahooTut2"

   4:     description <<

   5:       Tutorial for Twilio Yahoo!/Local Weather Mashup

   6:     >>

   7:     author "Craig Burton"

   8:     logging on

   9:   }


  11:   dispatch {

  12:   }


  14:   global {

  15:   //The "MostViewedArray" dataset is from the Yahoo! RSS Feed of the most viewed stories

  16:   //it updates every 60 seconds

  17:   dataset MostViewedArray:RSS <- "" cachable for 1 minute;


  19:   //The "bitly" datasource is the URI for the bitly RESTful API.

  20:   datasource bitly <- "" cachable for 1 minute;


  22:   //The "weather" datasource is a weather search service provided by the government

  23:   datasource weather:XML <- ""


  25:   //Note:

  26:   //These are great examples to help distinguish a dataset from a datasource.

  27:   //The datasources get modified in the rules in the "pre" statement for subsequent rules.

  28:   //The dataset is not modified but used as is throughout the app.


  30:   }



  33:     rule TwilioYahoo is active {


  35:     //The "mostviewedstories" webhook is the URL being given to Twilio to read to

  36:     //the caller of the Twilio developer phone number. The URL is automatically

  37:     //generated by Kynetx and looks like:

  38:     //

  39:     //Put this URL in the URL field of your Twilio developer phone number.

  40:     select when webhook mostviewedstories

  41:     pre {

  42:       //Since Twilio can't be called in a foreach statement,

  43:       //we have to collect the titles to be spoken back on the

  44:       //the phone call one by one. You will see this in the XML

  45:       //emmited to Twilio.

  46:       //Note:Kynetx JSON starts counting with a "0".

  47:       StoryTitle1 = MostViewedArray.pick("$.[0]..title.$t");

  48:       StoryTitle2=MostViewedArray.pick("$.[1]..title.$t");

  49:       StoryTitle3=MostViewedArray.pick("$.[2]..title.$t");

  50:       StoryTitle4 = MostViewedArray.pick("$.[3]..title.$t");

  51:       StoryTitle5=MostViewedArray.pick("$.[4]..title.$t");

  52:       StoryTitle6=MostViewedArray.pick("$.[5]..title.$t");

  53:       StoryTitle7 = MostViewedArray.pick("$.[6]..title.$t");

  54:       StoryTitle8=MostViewedArray.pick("$.[7]..title.$t");

  55:       StoryTitle9=MostViewedArray.pick("$.[8]..title.$t");



  58:       //This is an example of directives emmited to Twilio.

  59:       //The format is specific to Twilio and

  60:       //more details can be found in the Twilio documentation.

  61:       //Note that the <Gather> </Gather> directive

  62:       //can contain nested <Play> and <Say> directives

  63:       //that play and intro and read the top stories and

  64:       //waits for a digit followed by the pound key.

  65:       //The gather statement is accompanied

  66:       //by an "action" URL--"the webhook"--created by this rule.

  67:       //The "numDigits" value is set to two so the caller can enter a number

  68:       //followed by the pound key.

  69:       //As soon as a choice is entered, the reading of top stories stops.

  70:       //The selection (the pound key gets dropped) is

  71:       //POSTed to the "action" url (the webhook)

  72:       //and moves on to the next rule.

  73:       //If no entry is made, the app times out, says goodbye and hangsup.

  74:       xmlresponse = <<

  75:         <?xml version="1.0" encoding="UTF-8" ?>

  76:         <Response>

  77:           <Play></Play>

  78:           <Gather action="" numDigits="2" finishOnKey="#">

  79:           <Say voice="woman">

  80:             Please enter your story choice one thru nine, followed by the pound key.

  81:             The number one story is, #{StoryTitle1},

  82:             The number two story is, #{StoryTitle2},

  83:             The number three story is, #{StoryTitle3},

  84:             The number four story is, #{StoryTitle4},

  85:             The number five story is, #{StoryTitle5},

  86:             The number six story is, #{StoryTitle6},

  87:             The number seven story is, #{StoryTitle7},

  88:             The number eight story is, #{StoryTitle8},

  89:             The number nine story is, #{StoryTitle9},

  90:           </Say>

  91:           </Gather>

  92:           <Say voice="woman">

  93:             No input was received. Goodbye!

  94:           </Say>  

  95:         </Response>

  96:       >>;

  97:     }

  98:     //The "send_directive" statement POSTS the xml data to the webhook defined

  99:     //with the "when webhook" statement. In this case "mostviewedstories."

 100:     //So here is what is happening: This rule sends the above "xmlresponse" to the

 101:     //webhook "mostviewedstories." The Gather directive tells Twilio

 102:     //where to post the call key input. Twilio will POST the response

 103:     //in the webhook "yourchoice."

 104:     //Note: Twilio is being informed of the URL

 105:     //before the webhook is actually created.

 106:     //The next rule generates the webhook, gets the Twilio POST

 107:     //and then POSTS the next set of directives for Twilio to the same

 108:     //"yourchoice" webhook.

 109:     //This back-to-back kind of interchange could continue as long as the app needs to.

 110:     send_directive("xml") with body = "#{xmlresponse}";

 111:  }


 113:   rule TwilioYahoo is active {


 115:     //The way to continue interacting with Twilio is to declare a new webhook

 116:     //and use it for exchanging

 117:     //the data I want Twilio to act on. The next rule uses

 118:     //the data in the continuing inbound phone call.

 119:     select when webhook yourchoice

 120:     pre {

 121:         //The 'event:param("Digits")' lets the app collect the callers input POSTed

 122:         //to the webhook.

 123:         //Note that the app uses two couner values: "JSONStoryNumber" and "Count."

 124:         //This is because Kynetx JSON starts counting with a zero.

 125:         //Note: Twilio interchange is case sensitive. If things aren't working, check

 126:         //the case of the app.

 127:         JsonStoryNumber=event:param("Digits")-1;

 128:         count=event:param("Digits");


 130:         //The following pick statement selects the story number "-1" from the caller.

 131:         //Again it is -1 because story number one is story number zero in JSON.

 132:         //Note: A known bug in this app is

 133:         //that if the caller enters a zero the app will not work as the JSON counter gets

 134:         //set to a -1.

 135:         StoryLink = MostViewedArray.pick("$.[#{JsonStoryNumber}]$t");


 137:         //The following is a powerful statement to the bitly service.

 138:         //Most url shortening services require the url be "encoded" to always work.

 139:         //You can usually get away with just sending plain text,

 140:         //but it WILL bite you sometime.

 141:         //This statement shows you how to automatically encode the data for any datasource.

 142:         //Pass the data in a hash with value pairs in quotes between curly brackets

 143:         //seperated by a colon.

 144:         //Note that 'StoryLink' is not in quotes because it is a variable.

 145:         //Of course you will need your own login and apiKey.

 146:         //Note:Bitly is also case sensitive.

 147:         BitlyLink1 = datasource:bitly ({"login":"craigburton","apiKey":"insert your api key here","longurl":StoryLink,"format":"json"});

 148:         ShortLink = BitlyLink1.pick("$..url");


 150:       //The next to three statements get high and low temperatures

 151:       //based on the caller ID info collected by Twilio.

 152:       //The key here is event:param("FromZip").

 153:       //This statement gets the zip code of the caller.

 154:       //The datasource statement in the pre block dynamically gets the data

 155:       //from the datasource defined in the global block.

 156:       //In other words, the following statement is assigning "CallerWeatherData"

 157:       //with info from the datasource based on the caller's zipcode.

 158:       CallerWeatherData = datasource:weather(event:param("FromZip"));


 160:       //The following pick statements assign the high and low temp values

 161:       //from datasource derived from the caller's zipcode.

 162:       maxtemp = CallerWeatherData.pick("$..temperature[0].value.$t");

 163:       mintemp = CallerWeatherData.pick("$..temperature[1].value.$t");


 165:         //The following Twilio directives send a text message to the caller

 166:         //with a bitly-based link to the Yahoo! story the caller picked.

 167:         //The app then tells the caller which story was chosen

 168:         //followed by the caller's city and state with high and low temperatures.

 169:         //The call concludes by playing a pre-recorded thank you message.

 170:         xmlresponse = <<

 171:           <?xml version="1.0" encoding="UTF-8" ?>

 172:          <Response>

 173:            <Sms> Yahoo! most viewed story number #{count}: #{ShortLink}</Sms>

 174:            <Say voice ="woman">

 175:               You chose story number #{count}.

 176:            </Say>

 177:            <Pause length = "1"/>

 178:            <Say voice ="woman">

 179:               The forecast for #{event:param("CallerCity")}, #{event:param("CallerState")} is a high temperature of #{maxtemp} and a low of #{mintemp}.

 180:            </Say>

 181:            <Pause length = "1"/>

 182:            <Play></Play>       

 183:          </Response>

 184:        >>;

 185: }

 186:         send_directive("xml") with body = "#{xmlresponse}";


 188: }

 189: }

Tags: ··