The basic concept for a Twitter bot is a daemon that checks for replies to a given user account and then parses the message as command input. The command input is used to build an iControl call to BIG-IP, which is then formatted and returned to the requesting Twitter user as a reply.

This implementation checks for new replies every 150 seconds and responds to a very limited subset of commands.

The Twitter bot listens for requests and sends replies, it does not implement the full set of Twitter capabilities such as following users or responding to direct messages. It is meant to serve as an example of how iControl can be used to easily integrate with new technologies.

The PHP script listed below can be run as a daemon from the command line or a cron job.

Example:  nohup php checktwitter.php &

The core PHP functions used to retrieve system information and statistics can be found in this article

[makedate.php]
<?
// This function returns the time 2 minutes ago for use as the “since” parameter in
// a Twitter request for replies 
// It is likely fragile. Use at your own risk. 
 
function getpasttime() {
$now = getdate();
 
$now['minutes'] = $now['minutes'] - 2;
if ($now['minutes'] < 0) {
     $now['minutes'] = 0;
     $now['hours'] = $now['hours'] - 1;
     if ($now['hours'] < 0) {
        $now['hours'] = 12;
     }
}
 
$newtime = mktime($now['hours'], $now['minutes'], $now['seconds'], $now['mon'], $now['mday'], $now['year']);
$rightnow = date('D, j M Y H:i:s T', $newtime);
return $rightnow;
}
 
?>
 
[checktwitter.php]
<?
require_once 'getstats.php';
require_once 'makedate.php';
require_once 'getsysinfo.php';
 
// See http://www.bin-co.com/php/scripts/load/
// Version : 1.00.A
// This function loads a URL and returns the content. It handles HTTP Basic Auth via 
// standard protocol format using the following format: 
// http://username:password@domain/path/to/resource
 
function load($url,$options=array('method'=>'get','return_info'=>false)) {
    $url_parts = parse_url($url);
    $info = array(//Currently only supported by curl.
        'http_code'    => 200
    );
    $response = '';
    
    $send_header = array(
        'Accept' => 'text/*',
        'User-Agent' => 'BinGet/1.00.A (http://www.bin-co.com/php/scripts/load/)'
    );
 
    ///////////////////////////// Curl /////////////////////////////////////
    //If curl is available, use curl to get the data.
    if(function_exists("curl_init") 
                and (!(isset($options['use']) and $options['use'] == 'fsocketopen'))) { //Don't user curl if it is specifically stated to user fsocketopen in the options
        if(isset($options['method']) and $options['method'] == 'post') {
            $page = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'];
        } else {
            $page = $url;
        }
 
        $ch = curl_init($url_parts['host']);
 
        curl_setopt($ch, CURLOPT_URL, $page);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Just return the data - not print the whole thing.
        curl_setopt($ch, CURLOPT_HEADER, true); //We need the headers
        curl_setopt($ch, CURLOPT_NOBODY, false); //The content - if true, will not download the contents
        if(isset($options['method']) and $options['method'] == 'post' and $url_parts['query']) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $url_parts['query']);
        }
        //Set the headers our spiders sends
        curl_setopt($ch, CURLOPT_USERAGENT, $send_header['User-Agent']); //The Name of the UserAgent we will be using ;)
        $custom_headers = array("Accept: " . $send_header['Accept'] );
        if(isset($options['modified_since']))
            array_push($custom_headers,"If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T',strtotime($options['modified_since'])));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $custom_headers);
 
        curl_setopt($ch, CURLOPT_COOKIEJAR, "cookie.txt"); //If ever needed...
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
 
        if(isset($url_parts['user']) and isset($url_parts['pass'])) {
            $custom_headers = array("Authorization: Basic ".base64_encode($url_parts['user'].':'.$url_parts['pass']));
            curl_setopt($ch, CURLOPT_HTTPHEADER, $custom_headers);
        }
 
        $response = curl_exec($ch);
        $info = curl_getinfo($ch); //Some information on the fetch
        curl_close($ch);
 
    //////////////////////////////////////////// FSockOpen //////////////////////////////
    } else { //If there is no curl, use fsocketopen
        if(isset($url_parts['query'])) {
            if(isset($options['method']) and $options['method'] == 'post')
                $page = $url_parts['path'];
            else
                $page = $url_parts['path'] . '?' . $url_parts['query'];
        } else {
            $page = $url_parts['path'];
        }
 
        $fp = fsockopen($url_parts['host'], 80, $errno, $errstr, 30);
        if ($fp) {
            $out = '';
            if(isset($options['method']) and $options['method'] == 'post' and isset($url_parts['query'])) {
                $out .= "POST $page HTTP/1.1\r\n";
            } else {
                $out .= "GET $page HTTP/1.0\r\n"; //HTTP/1.0 is much easier to handle than HTTP/1.1
            }
            $out .= "Host: $url_parts[host]\r\n";
            $out .= "Accept: $send_header[Accept]\r\n";
            $out .= "User-Agent: {$send_header['User-Agent']}\r\n";
            if(isset($options['modified_since']))
                $out .= "If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T',strtotime($options['modified_since'])) ."\r\n";
 
            $out .= "Connection: Close\r\n";
            
            //HTTP Basic Authorization support
            if(isset($url_parts['user']) and isset($url_parts['pass'])) {
                $out .= "Authorization: Basic ".base64_encode($url_parts['user'].':'.$url_parts['pass']) . "\r\n";
            }
 
            //If the request is post - pass the data in a special way.
            if(isset($options['method']) and $options['method'] == 'post' and $url_parts['query']) {
                $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
                $out .= 'Content-Length: ' . strlen($url_parts['query']) . "\r\n";
                $out .= "\r\n" . $url_parts['query'];
            }
            $out .= "\r\n";
 
            fwrite($fp, $out);
            while (!feof($fp)) {
                $response .= fgets($fp, 128);
            }
            fclose($fp);
        }
    }
 
    //Get the headers in an associative array
    $headers = array();
 
    if($info['http_code'] == 404) {
        $body = "";
        $headers['Status'] = 404;
    } else {
        //Seperate header and content
        $separator_position = strpos($response,"\r\n\r\n");
        $header_text = substr($response,0,$separator_position);
        $body = substr($response,$separator_position+4);
        
        foreach(explode("\n",$header_text) as $line) {
            $parts = explode(": ",$line);
            if(count($parts) == 2) $headers[$parts[0]] = chop($parts[1]);
        }
    }
 
    if($options['return_info']) return array('headers' => $headers, 'body' => $body, 'info' => $info);
    return $body;
}
 
// Main body 
 
while (true) {
 
   $DEBUG = 0;
 
   // We don’t want to repeat responses so we’ll figure out the current time and 
   // subtract 2 minutes (our check interval) and format it according to the Twitter
   // API requirements as an HTTP RFC compliant date string 
   // In PHP 5.1 and higher the function http_date can be used instead
   $since = getpasttime();
   $since = urlencode($since);
 
   $url = "http://<username>:<password>@twitter.com/statuses/replies.xml?since=$since";
 
   if ($DEBUG == 1)
      echo "The url is: $url<br/>";
 
   $contents = load($url);
 
   if ($DEBUG == 1)
      echo "$contents<br/><br/>";
 
   // Using SimpleXMLElement to parse the response from Twitter into something usable 
   $xml = new SimpleXMLElement($contents);
  
   // the response is an array of statuses, with each status containing information about
   // the “reply”. We care about the request (text) the id (id) and the sender (screen_name) 
   // so we can determine what the request is for and send a response to the right twitter
   // user 
   // Commands accepted are:
   //    stats ALL    -- returns total of all requests 
   //    stats 200    -- returns number of 200 responses
   //    stats 400    -- returns number of 400 responses
   //    stats 500    -- returns number of 500 responses
   //    stats GET    -- returns number of GET requests handled
   //    stats POST   -- returns number of POST requests handled
   //    info         -- returns the version information 
 
   foreach ($xml->children() as $child) {
      $cmd = $child->text;
      if ($DEBUG == 1)
         echo "command was: $cmd<br/>";
 
      $whoasked = $child->user->screen_name;
      $id = $child->id;
 
      $req = explode(' ', $cmd);
 
      if ($DEBUG == 1) {
         foreach ($req as $ar) 
            echo "request split: $ar<br/>";
      }
     
      $reqType = "ALL";
 
      if ($req[1] == "stats") {
         if (count($req) > 2) {
            if ($DEBUG == 1)
                echo "setting request type to $req[2]<br/>";
            $reqType = $req[2];
         } 
         $stats = getstats($reqType);
      } else
      if ($req[1] == "info") {
         $stats = getversion();
      }
 
      if ($DEBUG == 1)
         echo "request type was: $reqType<br/>";
 
      $updateurl = "http://twitter.com/statuses/update.xml";
      $sendback = "in_reply_to_status_id=$id&status=@$whoasked $stats";
 
      $ContentLength = strlen($sendback);
 
      // build the HTTP Basic Auth info to insert into header 
      $auth = "<username>:<password>";
      $auth = base64_encode($auth);
 
      // build the HTTP request 
      $ReqHeader =
      "POST $updateurl HTTP/1.1\n".
      "Host: twitter.com\n".
      "Content-Type: application/x-www-form-urlencoded\n".
      "Content-Length: $ContentLength\n".
      "Authorization: Basic $auth\n\n".
      "$sendback\n";
 
      // open a connection to twitter and send the request
      $socket = fsockopen('twitter.com', 80, &$errno, &$errstr);
      $idx = 0;
      fputs($socket, $ReqHeader);
      while (!feof($socket))
         $Result[$idx++] = fgets($socket, 128);
  
      // spit out the response 
      foreach ($Result as $a) {
         echo "$a</br>";
      }
 
      if ($DEBUG == 1) 
         echo $sendback;
   }
 
   // sleep for 150 seconds and do it again 
   // rate limiting for API requests may prevent the daemon from receiving/sending replies
   // 150 seconds results in less than 30 calls per hour which should be under Twitter's rate limits 
   sleep(150);
 
} // end while (true) 
 
?>