You are here

sms_txtlocal.module in SMS Framework 6

Txtlocal gateway module for Drupal SMS Framework. Outbound+Inbound

For inbound messaging you must configure Txtlocal to send messages to:

  • http(s)://yourhost.example.com/sms/txtlocal/receiver

For receipts to work you must configure Txtlocal to send receipts to:

  • http(s)://yourhost.example.com/sms/txtlocal/receipt

It is recommended that Clean URLs are on if you are using WAP PUSH, receipt functions or anything else that will handle a URL.

The send callback in this module supports several options, including message sender. Please see sms_txtlocal_send()

@package sms @subpackage sms_txtlocal

File

modules/sms_txtlocal/sms_txtlocal.module
View source
<?php

/**
 * @file
 * Txtlocal gateway module for Drupal SMS Framework. Outbound+Inbound
 *
 * For inbound messaging you must configure Txtlocal to send messages to:
 *  - http(s)://yourhost.example.com/sms/txtlocal/receiver
 *
 * For receipts to work you must configure Txtlocal to send receipts to:
 *  - http(s)://yourhost.example.com/sms/txtlocal/receipt
 *
 * It is recommended that Clean URLs are on if you are using WAP PUSH, receipt
 * functions or anything else that will handle a URL.
 *
 * The send callback in this module supports several options, including message
 * sender. Please see sms_txtlocal_send()
 *
 * @package sms
 * @subpackage sms_txtlocal
 */

/**
 * Implement hook_gateway_info()
 *
 * @return
 *   SMS Framework gateway info array
 *
 * @ingroup hooks
 */
function sms_txtlocal_gateway_info() {
  return array(
    'txtlocal' => array(
      'name' => 'Txtlocal',
      'send' => 'sms_txtlocal_send',
      'send form' => 'sms_txtlocal_send_form',
      'configure form' => 'sms_txtlocal_admin_form',
      'message_status_codes' => 'sms_txtlocal_message_status_codes',
    ),
  );
}

/**
 * Implement hook_menu()
 *
 * @return
 *   Drupal menu item array
 *
 * @ingroup hooks
 */
function sms_txtlocal_menu() {
  $items = array();
  $items['sms/txtlocal/receiver'] = array(
    'title' => 'Txtlocal SMS message receiver',
    'page callback' => 'sms_txtlocal_receive_message',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['sms/txtlocal/receipt'] = array(
    'title' => 'Txtlocal SMS receipt receiver',
    'page callback' => 'sms_txtlocal_receive_receipt',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Configuration form for gateway module
 *
 * @param $configuration
 *
 * @return
 *   Drupal form array
 */
function sms_txtlocal_admin_form($configuration) {
  $form['sms_txtlocal_balance'] = array(
    '#type' => 'item',
    '#title' => t('Current balance'),
    '#value' => sms_txtlocal_balance(),
  );
  $form['sms_txtlocal_user'] = array(
    '#type' => 'textfield',
    '#title' => t('Username'),
    '#description' => t('The username of your Txtlocal account.'),
    '#size' => 40,
    '#maxlength' => 255,
    '#default_value' => $configuration['sms_txtlocal_user'],
  );
  $form['sms_txtlocal_password'] = array(
    '#type' => 'textfield',
    '#title' => t('Password'),
    '#description' => t('The current password on your Txtlocal account.'),
    '#size' => 30,
    '#maxlength' => 64,
    '#default_value' => $configuration['sms_txtlocal_password'],
  );
  $form['sms_txtlocal_defaultsender'] = array(
    '#type' => 'textfield',
    '#title' => t('Default sender of SMS messages'),
    '#description' => t('Required min=3 max=11 chars. May be an MSISDN or text. Will be used only if the SMS send call does not specify a sender.'),
    '#size' => 11,
    '#maxlength' => 11,
    '#default_value' => $configuration['sms_txtlocal_defaultsender'] ? $configuration['sms_txtlocal_defaultsender'] : 'anonymous',
  );
  $form['sms_txtlocal_receipts'] = array(
    '#type' => 'checkbox',
    '#title' => t('Request message delivery receipts'),
    '#description' => t('Click to enable receipt requests when messages are sent.'),
    '#default_value' => $configuration['sms_txtlocal_receipts'] ? $configuration['sms_txtlocal_receipts'] : FALSE,
  );
  $form['sms_txtlocal_ssl'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use SSL encyption'),
    '#description' => t('SSL is recommended so you should at least try this. Requires that PHP has enabled OpenSSL support.'),
    '#default_value' => $configuration['sms_txtlocal_ssl'] ? $configuration['sms_txtlocal_ssl'] : FALSE,
  );
  $form['sms_txtlocal_test'] = array(
    '#type' => 'checkbox',
    '#title' => t('Test mode'),
    '#description' => t('If set, txtlocal will not actually perform actions and will not deduct credit.'),
    '#default_value' => $configuration['sms_txtlocal_test'] ? $configuration['sms_txtlocal_test'] : FALSE,
  );
  return $form;
}

/**
 * Validates the submission of the configuration form.
 *
 * @param $form
 * @param $form_state
 */
function sms_txtlocal_admin_form_validate($form, &$form_state) {
  if (empty($form_state['values']['sms_txtlocal_user'])) {
    form_set_error('', t('You must specify a username.'));
  }
  if (empty($form_state['values']['sms_txtlocal_password'])) {
    form_set_error('', t('You must specify a password.'));
  }
  if (strlen($form_state['values']['sms_txtlocal_defaultsender']) < 3) {
    form_set_error('', t('The default sender must be at least 3 characters.'));
  }
}

/**
 * Returns custom additions to be added to the send forms
 *
 * @return
 *   Drupal form array
 */
function sms_txtlocal_send_form() {
  $form = array();
  return $form;
}

/**
 * Callback for sending messages.
 *
 * Options for this send function: see also sms_txtlocal_command()
 *  - sender - The sender of the message. MSISDN or text string. Min=3, max=11 chars.
 *  - reference - Message reference tag (to appear on any receipt).
 *  - delaymins - Minutes to delay message send.
 *  - url - Full URL for a WAP PUSH message.
 *
 * @param $number
 *   MSISDN of message recipient. Expected to include the country code prefix.
 * @param $message
 *   Message body text.
 * @param $options
 *   Options array from SMS Framework.
 *
 * @return
 *   Response from sms_txtlocal_command()
 */
function sms_txtlocal_send($number, $message, $options) {
  return sms_txtlocal_command('sendmsg', array(
    'number' => $number,
    'message' => $message,
    'options' => $options,
  ));
}

/**
 * Get account balance
 *
 * @return
 *   Balance text
 */
function sms_txtlocal_balance() {
  $result = sms_txtlocal_command('getbalance');
  return $result['gateway_status_text'];
}

/**
 * Executes a command using the Txtlocal API
 *
 * data array fields:
 *  - number - MSISDN of message recipient. Purely numeric and must begin with intl prefix, eg. 4477121231234. May be a comma-separated list.
 *  - message - Message text. Max 612 chars (4x SMS). Use %n for newline.
 *  - sender - Optional: Sender ID may be an MSISDN or a string. Min=3, max=11 chars.
 *  - url - Optional: May be used to send a WAP PUSH. 'url' param for txtlocal.
 *  - reference - Optional: Reference tag to apply to message. Will appear on any receipt. 'custom' param for txtlocal.
 *  - delaymins - Optional: Delay message sending by N minutes. Formatted into a future timestamp for 'shed' param in txtlocal.
 *
 * @param $command
 *   One of 'sendmsg' or 'getbalance'.
 * @param $data
 *   All data required to perform the command.
 * @param $config
 *   Configuration parameters.
 *
 * @return
 *   Whether the command succeeded or not.
 */
function sms_txtlocal_command($command = 'sendmsg', $data = array(), $config = NULL) {
  $gateway = sms_gateways('gateway', 'txtlocal');

  // Get config
  if ($config == NULL) {
    $config = $gateway['configuration'];
  }

  // SSL
  if ($config['sms_txtlocal_ssl'] === 1) {
    $scheme = 'https';
  }
  else {
    $scheme = 'http';
  }

  // Test mode
  if ($config['sms_txtlocal_test']) {
    $test = '1';
  }
  else {
    $test = '0';
  }

  // Preparing the URLs
  $url = $scheme . "://www.txtlocal.com/";
  $url_send = $url . "sendsmspost.php";
  $url_balance = $url . "getcredits.php";
  switch ($command) {
    case 'sendmsg':

      // Txtlocal requires us to specify a sender
      if (isset($data) && array_key_exists('options', $data) && array_key_exists('sender', $data['options'])) {
        $sender = $data['options']['sender'];
      }
      else {
        $sender = $config['sms_txtlocal_defaultsender'];
      }

      // Prepare required arguments
      $post_data = array(
        'uname' => $config['sms_txtlocal_user'],
        'pword' => $config['sms_txtlocal_password'],
        'message' => $data['message'],
        'from' => $sender,
        'selectednums' => $data['number'],
        'info' => '1',
        // We need this debug on to realise errors from txtlocal.
        'test' => $test,
      );

      // Request delivery receipts
      if ($config['sms_txtlocal_receipts']) {
        $post_data['rcpurl'] = url('sms/txtlocal/receipt', array(
          'absolute' => TRUE,
        ));
      }

      // Add any optional arguments
      if (isset($data) && array_key_exists('options', $data)) {
        if (array_key_exists('url', $data['options'])) {
          $post_data['url'] = $data['options']['url'];
        }
        if (array_key_exists('reference', $data['options'])) {
          $post_data['custom'] = $data['options']['reference'];
        }
        if (array_key_exists('delaymins', $data['options']) && $data['options']['delaymins'] > 5) {
          $delay_until = gmmktime() + $data['options']['delaymins'] * 60;
          $post_data['shed'] = date('Y-m-d-G-i-s', $delay_until);
        }
      }

      // Run the command
      // I tried to use http_build_query() here, but it kept adding '&' as
      // '&amp;' which killed the query.
      foreach ($post_data as $key => $value) {
        $content .= $key . "=" . $value . "&";
      }
      $headers = array(
        'Content-Type' => 'application/x-www-form-urlencoded',
      );
      $http_result = drupal_http_request($url_send, $headers, 'POST', $content);
      break;
    case 'getbalance':
      $post_data = array(
        'uname' => $config['sms_txtlocal_user'],
        'pword' => $config['sms_txtlocal_password'],
      );

      // I tried to use http_build_query() here, but it kept adding '&' as
      // '&amp;' which killed the query.
      foreach ($post_data as $key => $value) {
        $content .= $key . "=" . $value . "&";
      }
      $headers = array(
        'Content-Type' => 'application/x-www-form-urlencoded',
      );
      $http_result = drupal_http_request($url_balance, $headers, 'POST', $content);
      break;
  }

  // Check for HTTP errors
  if ($http_result->error) {
    return array(
      'status' => FALSE,
      'message' => t('An error occured during the HTTP request: @error', array(
        '@error' => $http_result->error,
      )),
    );
  }
  if ($http_result->data) {

    // Check for txtlocal errors
    if (strpos($http_result->data, 'ERROR') !== FALSE) {

      // There was an error
      $result = array(
        'status' => FALSE,
        'status_code' => SMS_GW_ERR_OTHER,
        // todo Try to map error codes
        'gateway_status_code' => '',
        // todo Try to map error codes
        'gateway_status_text' => $http_result->data,
      );
    }
    else {

      // Prepare a good response array
      $result = array(
        'status' => TRUE,
        'status_code' => SMS_GW_OK,
        'gateway_status_code' => '',
        // todo Try to map error codes
        'gateway_status_text' => $http_result->data,
      );
    }
  }
  return $result;
}

/**
 * Receive an SMS message and pass it into the SMS Framework
 */
function sms_txtlocal_receive_message() {
  $number = $_REQUEST['sender'];
  $message = $_REQUEST['content'];
  $options = array();

  // Define raw gateway response parameters
  $options['gateway_params'] = array();
  if (array_key_exists('inNumber', $_REQUEST) && !empty($_REQUEST['inNumber'])) {
    $options['gateway_params']['inNumber'] = $_REQUEST['inNumber'];
  }

  // Define message receiver if possible
  if (array_key_exists('inNumber', $_REQUEST) && !empty($_REQUEST['inNumber'])) {
    $options['receiver'] = $_REQUEST['inNumber'];
  }
  sms_incoming($number, $message, $options);
}

/**
 * Receive a message send receipt from txtlocal
 *
 * Will generate an $options array with the following variables:
 *  - reference - A message reference code, if set on message send.
 *  - gateway_status - A txtlocal message status code.
 *  - gateway_status_text - A text string associated with the gateway_status.
 *  - customID - Same as reference. txtlocal param: 'customID'
 *
 * See http://www.txtlocal.co.uk/sms-gateway-demo.php
 */
function sms_txtlocal_receive_receipt() {
  if ($config['sms_txtlocal_receipts']) {
    $number = array_key_exists('number', $_REQUEST) ? $_REQUEST['number'] : NULL;
    $reference = array_key_exists('customID', $_REQUEST) ? $_REQUEST['customID'] : NULL;
    $gw_msg_status_code = array_key_exists('status', $_REQUEST) ? $_REQUEST['status'] : NULL;
    $options = array();

    // Define raw gateway receipt call parameters
    $options['gateway_params'] = array();
    if (array_key_exists('number', $_REQUEST) && !empty($_REQUEST['number'])) {
      $options['gateway_params']['number'] = $_REQUEST['number'];
    }
    if (array_key_exists('customID', $_REQUEST) && !empty($_REQUEST['customID'])) {
      $options['gateway_params']['customID'] = $_REQUEST['customID'];
    }

    // Define message receiver and reference in options array
    $options['reference'] = $reference;

    // Get framework message status code and Clickatell status text
    $status = sms_txtlocal_map_message_status_code($gw_msg_status_code);
    $gw_msg_status_codes = sms_txtlocal_message_status_codes();
    $gw_msg_status_text = $gw_msg_status_codes[$gw_msg_status_code];

    // Define gateway-specific status (code) and text (success/error message)
    $options['gateway_message_status'] = $gw_msg_status_code;
    $options['gateway_message_status_text'] = $gw_msg_status_text;

    // Invoke the SMS Framework receipt handler
    sms_receipt($number, $reference, $status, $options);
  }
}

/**
 * Map a Txtlocal message status code to an SMS Framework message status code
 *
 * @return
 *   SMS Framework message status code.
 */
function sms_txtlocal_map_message_status_code($code) {
  switch ($code) {
    case 'D':
      return SMS_MSG_STATUS_DELIVERED;
    case 'I':
      return SMS_MSG_STATUS_ERROR;
    case 'U':
      return SMS_MSG_STATUS_EXPIRED;
  }
}

/**
 * Returns an array of message status codes and strings that are generated by the txtlocal gateway
 * 
 * @return array Associative array of message status codes and text strings.
 */
function sms_txtlocal_message_status_codes() {
  return array(
    'D' => 'Delivered',
    'I' => 'Invalid',
    'U' => 'Undelivered after 72 hours',
  );
}

Functions

Namesort descending Description
sms_txtlocal_admin_form Configuration form for gateway module
sms_txtlocal_admin_form_validate Validates the submission of the configuration form.
sms_txtlocal_balance Get account balance
sms_txtlocal_command Executes a command using the Txtlocal API
sms_txtlocal_gateway_info Implement hook_gateway_info()
sms_txtlocal_map_message_status_code Map a Txtlocal message status code to an SMS Framework message status code
sms_txtlocal_menu Implement hook_menu()
sms_txtlocal_message_status_codes Returns an array of message status codes and strings that are generated by the txtlocal gateway
sms_txtlocal_receive_message Receive an SMS message and pass it into the SMS Framework
sms_txtlocal_receive_receipt Receive a message send receipt from txtlocal
sms_txtlocal_send Callback for sending messages.
sms_txtlocal_send_form Returns custom additions to be added to the send forms