≡ Menu

Twitterbot, PHP, OAuth, and Program-O

Since opening it’s API, Twitter has been an ideal landscape for chatbots.  A couple of years ago, experienced developers made successful chatbots and blogged about them.  However, since Twitter went to the OAuth/REST API, the blog entries and how-to tutorials are dated.  Three weekends ago, I created my own twitterbot: [AI]mee.  [AI]mee does a few things currently: she will follow users that Twitterbot follow her, she will respond to direct Twitter messages, and she will respond to public twitter mentions involving her (‘@grassbrig’).  If you’re on Twitter, feel free to say ‘Hi’ to [AI]mee.

I made the twitterbot with the following tools:

  • Abraham Williams OAuth PHP Library which authenticates and provides an object-oriented framework for the REST calls.
  • Elizabeth Perreau’s Program-O that does the heavy-lifting of the AI’s input/output.
  • cron to automate the process.

Here’s a very rough and brief guide, subject to frequent updates:

1) Create a new Twitter application at http://dev.twitter.com.  Make sure that application is registered to read/write/direct messages.  Have twitter create the access tokens (‘consumer key’, ‘consumer secret’, ‘access token’, and ‘access token secret’).  These four components make up the authentication mechanism now preferred by Twitter and will be used by Abraham’s OAuth library to connect the application.

2) Download Abraham’s OAuth PHP Library, and unzip it.  I prefer $HOME/cron/twitter/twitteroauth/ to keep things tidy.

3) Download Program-O.  Program-O is a great project, but I did have a few installation hiccups which were isolated to SQL calls that PHP 5.3.3 complained about not being properly escaped on line 117 in the config/install_config.php.  After Program-O is installed, follow the installation guide for v2 and you should otherwise be fine.  Also, download Google’s latest AIML files from the ALICE Foundation.  You’ll use them to populate Program-O’s AI engine.

4) Use cron to call this php script.  You’ll need to tweak it to match your situation:

$db_host = 'localhost'; /*Your db host name here, probably 'localhost'*/
$db_user = 'yourusernamehere'; /*Your database user name here*/
$db_pw = 'yourpasswordhere'; /*Your password goes here*/
$db = 'your database name'; /*Your database name goes here*/
/* Twitter keys & secrets here */
$consumer_key = 'replaceme';
$consumer_secret = 'replaceme';
$access_token = 'replaceme';
$access_token_secret = 'replaceme';
/*** This is where we take the input.  You'll need to replace the url string with the url where program is installed. ***/
$url=curl_init('http://yourhost/program-o/gui/plain/index.php?say='.$text.'&submit=say&convo_id='.$msg_id.'&bot_id=1&format=html');
curl_setopt($url, CURLOPT_HEADER, 0);
mysql_connect($db_host,$db_user,$db_pw) or die('Could not connect to database');
mysql_select_db($db) or die('Could not select database');
$file_name="./dm_grassbrig.log";
require_once('./twitteroauth/twitteroauth.php');
$oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret);
$oauth--->useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9'; /* Avoids API penalties */
$credentials = $oauth->get('account/verify_credentials');
echo 'Connected as @' . $credentials->screen_name . '\n';
$remaining = $oauth->get('account/rate_limit_status');
echo "Current API hits remaining: {$remaining->remaining_hits}.\n";
$since_id=file_get_contents($file_name);
function make_reply()
{
	/*** This is clearly a hack ***/
	$sql="SELECT * FROM conversation_log ORDER BY id DESC LIMIT 1";
	$rs=mysql_query($sql) or die (mysql_error());
	$row=mysql_fetch_array($rs);
	$reply=$row['response'];
	return $reply;
	/***        End hack        ***/
}
$direct_msg = $oauth->get('direct_messages', array('cursor' => -1));
foreach ($direct_msg as $msg)
{
	sleep(rand(5,20));  /* Makes the bot a bit more human like */
	$user=$msg->sender_screen_name;
	$rtext=$msg->text;
	$text=urlencode($rtext);
	$msg_id=$msg->id;
	curl_exec($url);
	curl_close($url);
	$grassbrig_reply=$oauth->post('direct_messages/new',array('screen_name'=>$user,'text'=>make_reply()));
	$grassbrig_destory=$oauth->post('direct_messages/destroy/'.$msg_id);
}

function reply_tweet()
{
	$tweetsql="SELECT * FROM conversation_log ORDER BY id DESC LIMIT 1";
	$tweetrs=mysql_query($tweetsql) or die (mysql_error());
	$tweetrow=mysql_fetch_array($tweetrs);
	$tweet_reply=$tweetrow['response'];
	return $tweet_reply;
}
$tweets2me=$oauth->get('http://search.twitter.com/search.json', array('q'=> @grassbrig, 'since_id' => $since_id))->results;
    if (isset($tweets2me[0]))
	{
        file_put_contents($file_name, $tweets2me[0]->id_str);
    }
foreach($tweets2me as $mytweets)
{
    sleep(rand(5,20));
    $tweetfromuser= '@' . $mytweets->from_user;
    if($mytweets->from_user != "grassbrig")
	{
       $rtweettext=$mytweets->text;
       $remove_user_text=str_replace("@grassbrig","","$rtweettext");
       $tweettext=urlencode($remove_user_text);
       $fulltweettext=$tweettext;
       $tweetid=$mytweets->id;
       curl_exec($url);
       curl_close($url);
        /*Output, hackety-hack-hack */
            $tweetsql="SELECT * FROM conversation_log ORDER BY id DESC LIMIT 1";
            $tweetrs=mysql_query($tweetsql) or die (mysql_error());
            $tweetrow=mysql_fetch_array($tweetrs);
            $bot_reply=$tweetrow['response'];
            if(ctype_space($bot_reply))
			{
                curl_exec($url);
                curl_close($url);
            }
        $tweet_reply="$tweetfromuser $bot_reply";
		/* End hack.  There must be an more elegant way. */
        if($bot_reply !=NULL)
		{
			$oauth->post('statuses/update', array('status'=>$tweet_reply));
        }
    }
}
/*This is the auto-follow routine*/
$followers = $oauth->get('followers/ids', array('cursor' => -1));
$followerIds = array();
foreach ($followers->ids as $i => $id)
{
        $followerIds[] = $id;
        if ($i == 99) break; // Deal with only the latest 100 followers.
}
$friends = $oauth->get('friends/ids', array('cursor' => -1));
$friendIds = array();
foreach ($friends->ids as $i => $id)
{
        $friendIds[] = $id;
        if ($i == 1999) break; // Deal with only the latest 2000 friends.
}

foreach ($followerIds as $id)
{
if (empty($friendIds) or !in_array($id, $friendIds))
	{
		$ret = $oauth->post('friendships/create', array('user_id' => $id));
	}
}

5) Set cron to run this script every half an hour or so.  Be careful not to annoy people or Twitter will suspend your account.

This script will be updated periodically. Let me know if it helps!