* @version 1.0 * @since 20 Aug 2014 17:20:09 */ class SMS { /* * Sends an SMS containing $text to the number given in $to. This is a wrapper for * the various SMS API provider methods. The provider used is defined * in `$cfg['sms_provider']` in _config.inc.php. * * @access public * @param string $to Recipient's mobile phone number. * @param string $text The message * @return null * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 16:35:10 */ static public function send($to, $text) { global $cfg, $twilio, $nexmo_sms; switch ($cfg['sms_provider']) { case 'twilio' : $twilio->account->messages->sendMessage('425-242-6153', $to, $text); break; case 'nexmo' : $nexmo_sms->sendText($to, '525549998248', $text); break; default : $app->logMsg(sprintf('Unknown SMS provider: %s', $cfg['sms_provider']), LOG_ERR, __FILE__, __LINE__); break; } } /* * Receives the request to our webhook from the various SMS API providers, setting the values in a consistent return value. * The provider used is defined in `$cfg['sms_provider']` in _config.inc.php. Works with GET and POST. * * @access public * @return array Array contining 'sender_number' and 'text' from the $_REQUEST response. * @author Quinn Comendant * @version 1.0 * @since 09 Nov 2013 17:09:57 */ static public function receive() { global $cfg, $twilio, $nexmo_sms; $app =& App::getInstance(); $app->logMsg(sprintf('Receiving SMS: %s', getDump(getFormData())), LOG_DEBUG, __FILE__, __LINE__); switch ($cfg['sms_provider']) { case 'twilio' : $sender = preg_replace('/\D/', '', getFormData('From')); return array( 'virtual_number' => getFormData('…'), // TODO: check key value. 'sender_number' => (is_numeric($sender) ? '+' . $sender : ''), 'text' => getFormData('Body'), ); case 'nexmo' : $sender = preg_replace('/\D/', '', getFormData('msisdn')); return array( 'virtual_number' => getFormData('to'), 'sender_number' => (is_numeric($sender) ? '+' . $sender : $sender), 'text' => getFormData('text'), 'keyword' => getFormData('keyword'), 'concat' => getFormData('concat'), 'concat-ref' => getFormData('concat-ref'), 'concat-total' => getFormData('concat-total'), 'concat-part' => getFormData('concat-part'), ); default : $app->logMsg(sprintf('Unknown SMS provider: %s', $cfg['sms_provider']), LOG_ERR, __FILE__, __LINE__); return false; } } /* * Nexmo can send messages in multiple parts if it is longer than 160 characters. * Because these will be delivered in subsequent GET requests, the parts must be * stored in a temporary db table and assembled once the last part is returned. * This method should be called on the GET variables submitted by the Nexmo client. * https://docs.nexmo.com/index.php/sms-api/handle-inbound-message * * @access public * @param array $sms Array of values returned by SMS::receive(). * @return mixed If the message is fully formed, the same values from $sms are returned. False is returned if the message is in multiple parts and not all of them have been received yet. * @author Quinn Comendant * @version 1.0 * @since 26 Nov 2014 19:20:58 */ static public function processMultipart($sms) { $app =& App::getInstance(); $db =& DB::getInstance(); // This method is Nexmo-specific. Check that, and also if the message is a long 'concatenated' Inbound. if ('nexmo' == $cfg['sms_provider'] && isset($sms['concat']) && $sms['concat']) { // This sms is part of a multipart message, save it temporarily to the db. $sms = array_map('trim', $sms); $db->query(" REPLACE INTO response_concat_tbl ( `concat-ref`, `concat-total`, `concat-part`, `text`, `added_datetime` ) VALUES ( '" . $db->escapeString($sms['concat-ref']) . "', '" . $db->escapeString($sms['concat-total']) . "', '" . $db->escapeString($sms['concat-part']) . "', '" . $db->escapeString($sms['text']) . "', NOW() ) "); $app->logMsg(sprintf('Received multipart SMS part %s of %s ref %s', $sms['concat-part'], $sms['concat-total'], $sms['concat-ref']), LOG_DEBUG, __FILE__, __LINE__); if ($sms['concat-total'] > $sms['concat-part']) { // Not all the parts have been received; return false to signal the fact we don't have a complete message yet. return false; } // Otherwise, it means the last part has just been received. Concatonate all the parts and return it. // Increase the max length returned by MySQL's GROUP_CONCAT function. $db->query("SET SESSION group_concat_max_len = 32000"); // Group the sms responses by concat-ref and return them as a concatonated string. $qid = $db->query(" SELECT GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ') FROM response_concat_tbl WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "' GROUP BY `concat-ref` "); list($sms['text']) = mysql_fetch_row($qid); // Delete the temporary records. $db->query(" DELETE FROM response_concat_tbl WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "' "); $app->logMsg(sprintf('Final multipart SMS ref %s: %s', $sms['concat-ref'], $sms['text']), LOG_DEBUG, __FILE__, __LINE__); } // If this is not a multipart message, the original sms data is returned. If it is a multipart message, we're returning here the fully-concatonated message. return $sms; } }