Spark+SMS Interop with Tropo
Playing with SMS and Spark and Tropo a bit for a way to get SMS support for numbers. Tropo provides the PSTN number for calling and SMS. When a call or SMS is received on the Tropo number, the Tropo script triggers an API on a different server that provides all of the relay actions. If you call the number the call is relayed to the Spark URI for an audio call. SMS creates a new room based on the caller ID and then anything between those two rooms are bounced back and forth. You can add people to the room as well, but because it uses the room title for the PSTN side, you have to leave the phonenumber entact.
The SMS has to be initiated from the PSTN side for now. You could create a room, but in order to change the title of the room it has to be a group so you’d have to invite the SMS bot and another bot or user in order to rename it. Later I’ll create a 1:1 dialog for the bot so I can tell it to start an SMS or a call outbound.
This started off as a bot, but there are restrictions on what the bot can see, so in a room you would have to @mention the bot each time you sad something. With that in mind I created a free account and grabbed that new users auth token and used it. Now the “bot” sees everything that’s going on in the room and you don’t have to mention it. Plus side if you’re looking at larger deployments you could create a DB of tropo numbers and spark ID relations and then the single bot would have access to all SMS conversations for logging reasons. If you like that kind of thing that is.
The main bot is stored on a normal ol web server and the tropo script hosted from tropo. You just need the tropo and spark auth tokens. There is also a webhook created on membership for the bot prevent high-jacking by not me. I’ll show it in another post.
Tropo+Spark Relay Script
<?php $sparkToken = "Stuff"; $tropoToken = "Other Stuff"; function send_to_spark($method,$uri,$data) { global $sparkToken; switch ($method) { case "get": $uri .= $data; $options = array( 'http' => array( 'header' => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/x-www-form-urlencoded\r\n", 'method' => 'GET', ), ); break; case "post": $options = array( 'http' => array( 'header' => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/json\r\n", 'method' => 'POST', 'content' => json_encode($data), ), ); break; } $context = stream_context_create($options); $result = json_decode(file_get_contents("https://api.ciscospark.com/v1/".$uri, false, $context)); return $result; } function send_to_tropo($smsNumber,$smsData) { global $tropoToken; $tropoData = json_encode(array("token"=>$tropoToken ,"smsNumber"=>$smsNumber ,"smsData"=>$smsData )); $options = array( 'http' => array( 'header' => "Content-type: application/json\r\nContent-Length: ".strlen($tropoData)."\r\n", 'method' => 'POST', 'content' => $tropoData, ), ); $context = stream_context_create($options); $result = json_decode(file_get_contents("https://api.tropo.com/1.0/sessions", false, $context)); return $result; } // Get api info info $jsonData = json_decode(strip_tags(file_get_contents("php://input"))); if ( isset($jsonData->tropo) ) { //Call is from tropo - Post to Spark Room $sparkMemberships = send_to_spark("get","rooms",""); foreach ( $sparkMemberships->items as $room ){ if ( preg_replace("/[^\d]/","", $room->title) == preg_replace("/[^\d]/","", $jsonData->tropo->callingNumber) ){ $roomId = $room->id; break; } } if ( ! isset($roomId) ) { $roomTitle = preg_replace("/^1(\d{3})(\d{3})(\d{4})/", 'SMS +1 (${1}) ${2}-${3}', $jsonData->tropo->callingNumber); $sparkData = array("title"=>$roomTitle ,"type"=>"group" ); $roomCreate = send_to_spark("post","rooms",$sparkData); $roomId = $roomCreate->id; } //add/readd me to the room $sparkData = array("roomId"=>$roomId ,"personEmail"=>"jsnipes@domain.com" ); $roomMember = @send_to_spark("post","memberships",$sparkData); //send SMS to Spark Room $sparkData = array("roomId"=>$roomId ,"text"=>$jsonData->tropo->text ); send_to_spark("post","messages",$sparkData); } elseif ( isset($jsonData->data) ) { $messageId = $jsonData->data->id; $sparkResponse = send_to_spark("get","messages/",$messageId); if ( $sparkResponse->personEmail == "sms@domain.org") { //Stop processing if the message is from the bot die(); } $sparkMessage = preg_replace("/^SMS/","", $sparkResponse->text); $sparkResponse = send_to_spark("get","rooms/",$sparkResponse->roomId); $sparkNumber = "+".preg_replace("/[^\d]/","",$sparkResponse->title); $tropoResponse = send_to_tropo($sparkNumber,$sparkMessage); } ?>
Tropo Script
<?php function send_to_spark_relay($data) { $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => "https://domain.org/sparkSMS.php", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => array( "cache-control: no-cache", "content-type: application/json", ), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); } if ( isset($smsNumber) ) { call($smsNumber, array("network" => "SMS") ); say($smsData); } else { if ( $currentCall->channel == "VOICE" ) { transfer("sip:jsnipes@domain.call.ciscospark.com"); } elseif ( $currentCall->channel == "TEXT" ) { $smsData = array("tropo"=>array( "callingNumber"=>$currentCall->callerID ,"callingName"=>$currentCall->callerName ,"text"=> $currentCall->initialText )); send_to_spark_relay($smsData); } } ?>