My Collab Lab

mycollablab.org

Spark+SMS Interop with Tropo

October 15, 2017 Webex

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);
    }
}
?>

botsmssparktropo

Spark Bot Security

email2spark bot using O365, MS Flow and Azure Functions


Jon Snipes
LinkedIn

My career progressed from head butcher to Collaboration CCIE. There isn’t much technically that carries over between professions, but 8 years of direct customer service experience and squeezing margin out of processes provided a solid base and direction for the rest of my career. My focus today is deep into collaboration messaging, voice and video with the expertise to develop processes and programmatic solutions to complex business problems.

Principal Architect at Cloverhound
CCIE Collaboration 51786
Cisco Webex Expert
2018-2021 Cisco Champion

Categories
  • Cloud (4)
  • Contact Center (1)
  • Development (1)
  • PHP (10)
  • Python (3)
  • Uncategorized (1)
  • Video (2)
  • Voice (12)
  • Voicemail (5)
  • Webex (7)
Recent Posts
  • Azure OAuth2.0 User Authentication December 4, 2021
  • Pass-through Gateway Routing and Dial Peer Groups December 4, 2021
  • Get Started Coding with Python: System Setup and Running Scripts December 4, 2021
  • Exporting CUCM to an Offline Local DB with Python December 4, 2021
  • Connecting to UCCX INFORMIX DB with Python on Linux December 4, 2021
Proudly powered by WordPress | Theme: Doo by ThemeVS.