NOTE: Twitter disabled xmpp/jabber/im capabilities a while ago. I am still leaving this tutorial online for informational purposes.

Twitter PHP Jabber Bot Tutorial


Hello. Thank you for reading this Twitter PHP Jabber Bot Tutorial. This page is simple and lacking web2.0 "shiny-ness" by design because writing code is not shiny. It's down and dirty... and that's why you're here, right?

Pre-requisites:

Knowledge of PHP
How to run a PHP script on your machine
Some Knowledge of Jabber/XMPP (i.e. how to create an account on a jabber server)
Knowledge of Twitter and its IM/Jabber commands

Step 1 - Create your bot's Twitter account

I will assume you know how to create a twitter account. This one will be used by your bot.

Step 2 - Create your bot's jabber account

Using an existing Jabber client (Windows: Exodus - Linux: Pidgin), you will need to create a jabber account for your bot. Make sure to remember your username and which jabber server you picked.

Step 3 - Register your bot's jabber account with Twitter

Once you login to Twitter using your bot's account, click on "Settings", then "Devices" tab, then under "Instant Messenger" enter in your bot's jabber account (e.g. username@jabber.org - but use the correct jabber server). Select "Jabber" in the drop down box. Click "Save", then copy the activation code and (using your jabber client) send twitter@twitter.com the activation code. You should receive a response like "Your device has been verified and is on." You are now activated and can now log off of the jabber client.

Step 3.5 - Make friends with yourself (optional)

This step is optional, but very very useful, so I recommend it.
If you have a jabber or gmail chat account you use personally, use your jabber client to sign in again with your bot's username and send your personal jabber/gmail account a message so that you will both be added to each other's rosters. This prevents having to handle roster/friend requests in the php code (makes things much simpler). The advantage of having your bot on your personal roster is that you can now control your bot with your personal account using some interesting commands (more on that later). You can now log off your bot again.

Step 4 - The Code!!!

First, you will need Steve Blinch's brilliant PHP Jabber Class. You can download it from here.
The version of Jabber Class with which I wrote the Twitter bots is 0.9, which you can get here.
Below is the code for example_twitter_bot.php, which you can also download here.
Save the code below as "example_twitter_bot.php" and put it in the same directory as the PHP Jabber Class files.
Please pay attention to the comments in the code; they are there to help you understand what is going on, and they also tell you what you need to change to customize your own bot!
Note: Windows users do not need the "#!/usr/local/bin/php -q" at the beginning of the file

#!/usr/local/bin/php -q
<?php
/* Twitter Jabber Class Example
 * by JazzyChad
 * based on the Jabber/PHP class whis is
 * Copyright 2002-2007, Steve Blinch
 * http://code.blitzaffe.com
 * ============================================================================
 *
 * DETAILS
 *
 * This code logs onto a jabber server and is able to communicate with the Twitter jabber bot
 * It can handle direct messages, normal tweets, @replies, and tracked messages
 */
 
// set your Jabber server hostname, username, and password here
define("JABBER_SERVER","jabber-server.tld");
define("JABBER_USERNAME","username");
define("JABBER_PASSWORD","password");

define("RUN_TIME",-1);    // set -1 for infinite runtime
define("CBK_FREQ",1);    // fire a callback event every second

define("BOTNAME""botname");  // set the twitter username that the bot uses
define("KILL_STRING""endthisbot"); //this is the kill string to end this bot remotely
define("JABBER_PRESENCE""I am connected to jabber!!"); //this is what your bot status will be to jabber server

set_time_limit(0); // tell php not to terminate script

// This class handles all events fired by the Jabber client class; you
// can optionally use individual functions instead of a class, but this
// method is a bit cleaner.
class TwitterBot {
    
    function 
TwitterBot(&$jab) {
        
$this->jab = &$jab;
        
$this->first_roster_update true;
        
        echo 
"Created!\n";
    }
    
    
// called when a connection to the Jabber server is established
    
function handleConnected() {
        echo 
"Connected!\n";
        
        
// now that we're connected, tell the Jabber class to login
        
echo "Authenticating ...\n";
        
$this->jab->login(JABBER_USERNAME,JABBER_PASSWORD);
    }
    
    
// called after a login to indicate the the login was successful
    
function handleAuthenticated() {
        echo 
"Authenticated!\n";
        
        
        echo 
"Fetching service list and roster ...\n";
        
        
// browser for transport gateways
        
$this->jab->browse();
        
        
// retrieve this user's roster
        
$this->jab->get_roster();
        
        
// set this user's presence
        
$this->jab->set_presence("",JABBER_PRESENCE);
    }
    
    
// called after a login to indicate that the login was NOT successful
    
function handleAuthFailure($code,$error) {
        echo 
"Authentication failure: $error ($code)\n";
        
        
// set terminated to TRUE in the Jabber class to tell it to exit
        
$this->jab->terminated true;
    }
    
    
// called periodically by the Jabber class to allow us to do our own processing
    
function handleHeartbeat() {
        echo 
".";  //change to whatever you want, this gets called every CBK_FREQ seconds
    
}
    
    
// called when an error is received from the Jabber server
    
function handleError($code,$error,$xmlns) {
        echo 
"Error: $error ($code)".($xmlns?" in $xmlns":"")."\n";
    }
    
    
// called when a message is received from a remote contact
    
function handleMessage($from,$to,$body,$subject,$thread,$id,$extended) {
        echo 
"Incoming message!\n";
        echo 
"From: $from\t\tTo: $to\n";
        echo 
"Subject: $subject\tThread; $thread\n";
        echo 
"Body: $body\n";
        echo 
"ID: $id\n";
        
var_dump($extended);
        echo 
"\n";
        
        
$this->last_message $body;
        
            
//these next two bits are remote control constructs that can either kill the bot, or tell it to send
            //a message to someone
    
            // I created this kill string so I could turn off the bot remotely using another jabber client (gmail chat, etc...)
            //if I send the KILL_STRING directly to this bot, it will end and go offline
            
if ($body == KILL_STRING) {
                
$this->jab->terminated true;
                return;
            }

            
//another remote control construct  here,  useful for telling the bot to send a message to a 
            //sepcified recipient by doing it from a remote jabber client
            //example: send:twitter@twitter.com:follow user (notice colons between sections)
            
if(substr($body05) == "send:") {
                list(
$send$to$msg) = explode(":"$body3);
                
$this->jab->message($to,"chat",NULL,$msg);
                return;
            }            

            
//this is really where you start handling messages from Twitter
            //if you really wanted to add "security" to make sure the messages are coming from twitter, you
            //could either wrap all of these in an if($from == "twitter@twitter.com") or change the logic in the 
            //is*Message() handler functions below
            
            //check to see if this tweet is a reply to this bot
            
if (strpos($body,"@" BOTNAME "")) {
                
$user $this->getUserFromReply($body);
                
$msg $this->getMsgFromReply($body);
                
                
//add any extra functionality for reply message handling here
            
}
            
            
//check to see if this is a direct message from twitter
            
if($this->isDirectMessage($body)) {
                
$user $this->getUserFromDirectMessage($body);
                
$msg $this->getMsgFromDirectMessage($body);
                
                
//add any extra functionality for direct message handling here
                
            
// end if isDirectMessage()

            //check if this is a tracked message from twitter
            
if($this->isTrackedMessage($body)) {
                
$user $this->getUserFromTrackedMessage($body);
                
$msg $this->GetMsgFromTrackedMessage($body);
                
                
//add any extra functionality for tracked message handling here

            
}

            
            
//it is important to note that I do not have any sort of handler for just normal tweets at this point
            //as only direct messages, replies, or tracked messages will create a valid $user and $msg variable.
            //if you want to add a "normal" tweet handler, you should insert it here before the next if statement
            //which will return if one of them is not set.

            
            //if the message or user is empty, return...
            
if ($msg == "" || $user == "") {
                
//abort!
                
return;
            }
            
            
// BEGIN COMMAND HANDLING
            //this is really where you start handling your defined commands for your twitter bot.
            //add/modify your commands to the if structure below in a similar manner
            
$msg strtolower($msg);
            if (
substr($msg0strlen("command1")) == "command1") {  //change "command1" twice
                
$this->handleCommand1($usertrim(substr($msgstrlen("command1")))); //change "command1"
                
            
} else if (substr($msg0strlen("command2")) == "command2") { //change "command2" twice
                
$this->handleCommand2($usertrim(substr($msgstrlen("command2")))); //change "command2"
                
            
} else {
                
// default action - this means that no good command was received by your bot
                //you could either send a help message, or do nothing
                
echo "no good command!";
            }

        
$this->last_msg_id $id;
        
$this->last_msg_from $from;
        
    
    } 
//end function handleMessage

        //this function will send a message to the twitter bot
        
function tweet($msg) {
                
$this->jab->message("twitter@twitter.com","chat",NULL,"$msg");
        }
    
    
    
//the following functions are helper functions to determine the type of tweet and
    //extract user and message info
    
    
        
function getUserFromReply($body) {
            list(
$user$note) = explode(":"$body2);
            
$user str_replace('('''$user);
            
$user str_replace(')'''$user);
            return 
$user;
        }
    
        function 
getMsgFromReply($body) {
            list(
$user$cmd) = explode(":"$body2);
            
$cmd trim(str_replace(','''$cmd));
            
$cmd trim(str_replace(':'''$cmd));
            
$cmd trim(str_replace('@' BOTNAME ''''$cmd)); // replace bot name with correct name
            
echo $cmd;
            return 
$cmd;
        }

        function 
isDirectMessage($body) {
            if(
substr($body012) == "Direct from ") {
                return 
1;
            } else {
                return 
0;
            }
        }
        
        function 
getUserFromDirectMessage($body) {
            list(
$user$msg) = explode(":"$body2);
            
$user trim(str_replace("Direct from """$user));
            return 
$user;
        } 
// end function
        
        
function getMsgFromDirectMessage($body) {
            list(
$user$msg) = explode(":"$body2);
            
$msg trim($msg);
            
$i strpos($msg"\n\nReply with 'd "$msg);
            
$msg substr($msg0$i);
            return 
$msg;
        } 
// end function


        
function isTrackedMessage($body) {
            list(
$user$msg) = explode(":"$body2);
            
//echo "user: $user msg: $msg\n";
            
if (!(strpos($user"(") === FALSE) && !(strpos($user")") === FALSE)) {
                
//echo "returned 1\n";
                
return 1;
            } else {
                
//echo "returned 0;\n";
                
return 0;
            }
            
//echo "not good!!\n";
        
}        
        
        function 
getUserFromTrackedMessage($body) {
            list(
$user$note) = explode(":"$body2);
            
$user str_replace('('''$user);
            
$user str_replace(')'''$user);
            return 
$user;
        }
    
        function 
getMsgFromTrackedMessage($body) {
            list(
$user$cmd) = explode(":"$body2);
            
$cmd trim(str_replace(','''$cmd));
            
$cmd trim(str_replace(':'''$cmd));
            
$cmd trim(str_replace('@madstatter'''$cmd));
            
//echo $cmd;
            
return $cmd;
        }
        
    
    
//below are the bot command handlers
        
        
function handleCommand1($user$args) {
        
            
//handle command here
            
$this->tweet("@$user you said command1 with args $args");
        
        }
        
        
        function 
handleCommand2($user$args) {
        
            
//handle command here
            
$this->tweet("@$user you said command2 with args $args");
        
        }


    
//the rest of these commands are handlers for various jabber events, you shouldn't need to edit them for normal functionality, but you are free to change them if you want

        
    
function _contact_info($contact) {
        return 
sprintf("Contact %s (JID %s) has status %s and message %s\n",$contact['name'],$contact['jid'],$contact['show'],$contact['status']);
    }
    
    function 
handleRosterUpdate($jid) {
        if (
$this->first_roster_update) {
            
// the first roster update indicates that the entire roster has been
            // downloaded for the first time
            
echo "Roster downloaded:\n";
            
            foreach (
$this->jab->roster as $k=>$contact) {
                echo 
$this->_contact_info($contact);
            }    
            
$this->first_roster_update false;
        } else {
            
// subsequent roster updates indicate changes for individual roster items
            
$contact $this->jab->roster[$jid];
            echo 
"Contact updated: " $this->_contact_info($contact);
        }
    }
    
    function 
handleDebug($msg,$level) {
        echo 
"DBG: $msg\n";
    }
    
}

// include the Jabber class
require_once("class_Jabber.php");

// create an instance of the Jabber class
$display_debug_info false;
$jab = new Jabber($display_debug_info);

// create an instance of our event handler class
$bot = new TwitterBot($jab);

// set handlers for the events we wish to be notified about
$jab->set_handler("connected",$bot,"handleConnected");
$jab->set_handler("authenticated",$bot,"handleAuthenticated");
$jab->set_handler("authfailure",$bot,"handleAuthFailure");
$jab->set_handler("heartbeat",$bot,"handleHeartbeat");
$jab->set_handler("error",$bot,"handleError");
$jab->set_handler("message_normal",$bot,"handleMessage");
$jab->set_handler("message_chat",$bot,"handleMessage");
$jab->set_handler("debug_log",$bot,"handleDebug");
$jab->set_handler("rosterupdate",$bot,"handleRosterUpdate");

echo 
"Connecting ...\n";

// connect to the Jabber server
if (!$jab->connect(JABBER_SERVER)) {
    die(
"Could not connect to the Jabber server!\n");
}

// now, tell the Jabber class to begin its execution loop
$jab->execute(CBK_FREQ,RUN_TIME);


// Note that we will not reach this point (and the execute() method will not
// return) until $jab->terminated is set to TRUE.  The execute() method simply
// loops, processing data from (and to) the Jabber server, and firing events
// (which are handled by our TwitterBot class) until we tell it to terminate.
//
// This event-based model will be familiar to programmers who have worked on
// desktop applications, particularly in Win32 environments.

echo "ended execution\n";

// disconnect from the Jabber server
$jab->disconnect();
echo 
"ending script...\n";
exit();
?>

Step 5 - Running the Bot

At this point, you can customize the code and rename the file to whatever you want. For simplicity, I will keep referring to the file as "example_twitter_bot.php", but just use whatever file you have created.

I am assuming you have php installed on your system and know how to use it from the command line.

Windows: Open up a command prompt and cd to your directory with example_twitter_bot.php
Type php example_twitter_bot.php and hit return.

Linux: Open a terminal and cd to your directory with example_twitter_bot.php
Give your script execute permissions (chmod u+x example_twitter_bot.php)
Type ./example_twitter_bot.php and hit return.

Your Twitter bot should now come to life!

Step 6 - There is no Step 6

Step 7 - Other stuff

If you did step 3.5 and added your bot to your personal jabber roster, here are a couple of things you can do:

Kill the bot: If your bot is doing unexpected things and you want to kill it remotely, just send the "kill string" to your bot, and it should die. This kill string is defined in the code with define("KILL_STRING", "whatever") near the top. Make sure to make this something unusual so ordinary people can't kill your bot.

Make the bot send a message: Say you want your bot to update it's twitter status with something new. You can send your bot a message like "send:twitter@twitter.com:here's my new status!" (with no quotes, and note the colons) which will cause your bot to send "here's my new status!" to twitter@twitter.com. Get it? This can be useful for many things, just be creative.

Whatever else you want: You can add more "remote control" commands to the code to do your bidding. It's up to you!

Step 8 - More to consider

The bot should run forever. On windows systems I have tried, it would randomly disconnect from the jabber server and the script would terminate. On the Linux systems I have running, they really do seem to run forever.

If you find something wrong with the code above, or it won't run because of a syntax error or something, please let me know! Nothing upsets me more than to find a tutorial online that ends up having errors right out of the box. I can be reached at chad at jazzychad dot com or on twitter @jazzychad. I want people to find this tutorial useful since I did not have a reference to go by when I was creating my bots. Please let me know how I can improve it.

My current bots: @rollthedice, @note2self, and @madstatter.

Thanks for reading this far! I hope this has helped you in learning more about twitter jabber bots!