Some of you who have been using elggVoices recently, may have noticed some rather odd looking shouts appearing in the channels.

Shouts beginning with “@” can be used to execute commands in a channel – especially useful when used remotely via SMS.

I thought it might be useful to give you a quick run down of the commands currently available. I say “currently”, since we are adding to this all the time.

SMS and Website

  • @status message: Updates your status on a channel.
  • @sleep on|off: Set your sleep status on and off, when @sleep is on you won’t receive SMS messages.
  • @location city: Updates your location.
  • @country country: Updates your country.

SMS only

  • @signup desired_username your_email: Send this to a channel’s SMS number and you will create a elggVoices account and automatically be signed up to the channel. Your password will be emailed to your email address.
  • @join: Send this to a channel’s SMS number will join the channel.

Just a few commands for now, but there will be more to come.

In yesterday’s article, we took a look at the server’s reply format. You now have all the information you need to construct the core of a library and start making server requests.

Here is some sample PHP code for making POST and GET style queries. This library will let you make a query, and return a stdClass object containing the serialised fields as described yesterday, from this you can build wrapper functions for the methods.

$API_CLIENT = new stdClass;

// Configure your client
$API_CLIENT->api_key = "your api key";
$API_CLIENT->secret = "your secret key";
$API_CLIENT->api_endpoint = "http://elggvoices.com/api/v1/";
$API_CLIENT->hmac_algo = 'sha256';
$API_CLIENT->postdata_hash_algo = 'md5';

// Status variables we can query later
$LAST_CALL = null;
$LAST_CALL_RAW = "";
$LAST_ERROR = null;

/**
* Generate our HMAC.
*/
function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "")
{
$ctx = hash_init($algo, HASH_HMAC, $secret_key);
hash_update($ctx, trim($time));
hash_update($ctx, trim($api_key));
hash_update($ctx, trim($get_variables));
if (trim($post_hash)!="") hash_update($ctx, trim($post_hash));
return hash_final($ctx);
}

/**
* Generate our POST hash.
*/
function calculate_posthash($postdata, $algo)
{
$ctx = hash_init($algo);
hash_update($ctx, $postdata);
return hash_final($ctx);
}

/**
* Serialise HTTP headers.
*/
function serialise_headers(array $headers)
{
$headers_str = "";
foreach ($headers as $k => $v)
$headers_str .= trim($k) . ": " . trim($v) . "\r\n";
return trim($headers_str);
}

/**
* Make a raw call.
* @param array $method Method call parameters.
* @param string $postdata Optional POST data.
* @param string $content_type The content type.
* @return stdClass
*/
function call(array $method, $postdata = "", $content_type = 'application/octet-stream')
{

// Get the config
global $API_CLIENT, $LAST_CALL, $LAST_CALL_RAW, $LAST_ERROR;

$headers = array();
$encoded_params = array();

$time = microtime(true); // Get the current time in microseconds
$request = ($postdata!="" ? "POST" : "GET"); // Get the request method, either post or get

// Hard code the format - we're using PHP, so lets use PHP serialisation.
$method['format'] = "php";

// URL encode all the parameters
foreach ($method as $k => $v){
$encoded_params[] = urlencode($k).'='.urlencode($v);
}

$params = implode('&', $encoded_params);

// Put together the query string
$url = $API_CLIENT->api_endpoint."?". $params;

// Construct headers
$posthash = "";
if ($request=='POST')
{
$posthash = calculate_posthash($postdata, $API_CLIENT->postdata_hash_algo);
$headers['X-Searunner-posthash'] = $posthash;
$headers['X-Searunner-posthash-algo'] = $API_CLIENT->postdata_hash_algo;
$headers['Content-type'] = $content_type;
$headers['Content-Length'] = strlen($postdata);
}

$headers['X-Searunner-apikey'] = $API_CLIENT->api_key;
$headers['X-Searunner-time'] = $time;
$headers['X-Searunner-hmac-algo'] = $API_CLIENT->hmac_algo;
$headers['X-Searunner-hmac'] = calculate_hmac($API_CLIENT->hmac_algo,
$time,
$API_CLIENT->api_key,
$API_CLIENT->secret,
$params,
$posthash
);

// Configure stream options
$opts = array(
'http'=>array(
'method'=> $request,
'header'=> serialise_headers($headers)
)
);

// If this is a post request then set the content
if ($request=='POST')
$opts['http']['content'] = $postdata;

// Set stream options
$context = stream_context_create($opts);

// Send the query and get the result and decode.
$LAST_CALL_RAW = file_get_contents($url, false, $context);
$LAST_CALL = unserialize($LAST_CALL_RAW);

if (($LAST_CALL) && ($LAST_CALL->status!=0)) // Check to see if this was an error
$LAST_ERROR = $LAST_CALL;

return $LAST_CALL; // Return a stdClass containing the API result
}

// Example GET call
$get = call(
array (
'method' => 'test.test',
'variable1' => 1,
'variable2' => "test string"
)
);

// Example POST call
$post = call(
array (
'method' => 'test.test',
'variable1' => 1,
'variable2' => "test string"
),
"Some post data"
);

// Output
echo "Example GET result: \n";
print_r($get);

echo "Example POST result: \n";
print_r($post);

On Friday we discussed how to construct an API request, today we will discuss how to handle the response that the server returns.

Server replies

The elggVoices server will respond with a result packet containing a number of fields. The format of this reply depends on the format requested. From the manual:

  • ‘status’: The status code, 0 for success or a non-zero error code.
  • ‘message’: If the status code is non-zero this contains a human readable
    explanation.
  • ‘result’: The mixed result of the execution, this may be a simple type
    (int, string etc), an array or even an object, depending on the API being
    called (see also result objects).
  • ‘runtime_errors’: If there have been any runtime errors picked up by
    the internal error handler during the execution of the command, these
    are given here in an array.

The server result will be returned, serialised in the requested format (xml,php or json).

Errors will often give details pointing to what went wrong, these can be helpful when debugging your application or when looking for support on the forums.

Have a look at this XML formatted error message, XML success messages will be of a similar format:

<SearunnerResult>
<status type="integer">-1</status>
<message type="string">Missing X-Searunner-apikey HTTP header</message>
<result type="string">exception 'APIException' with message 'Missing X-Searunner-apikey HTTP header' in /home/liveshouts/html/lib/api/validation.php:109
Stack trace:
#0 /home/liveshouts/html/api/endpoints/rest.php(19): get_and_validate_api_headers()
#1 {main}</result>
<runtime_errors type="array">
<array_item name="0" type="string" >DEBUG: 2008-02-24 18:38:19 (UTC): & quot;Undefined index: method& quot;
in file /home/liveshouts/html/lib/engine/input.php (line 30)</array_item>
<array_item name="1" type="string">DEBUG: 2008-02-24 18:38:19 (UTC): &quot;Undefined index: HTTP_X_SEARUNNER_APIKEY&quot; in file /home/liveshouts/html/lib/api/validation.php (line 107)</array_item>
</runtime_errors>
</SearunnerResult>