How to Write a Program That Tweets Automatically

|

UPDATE: I did this better in Python here

A while back I wrote a post on how to write a program that would at-reply Twitter users when certain keywords were found in their tweets. The program, or Twitter bot, had some major shortcomings. It only ran once an hour, replied to only one person when it ran, and wasn’t selective enough in its at-replies. My new Twitter bot still has some shortcomings but is much improved.

My old post was a convoluted and incomplete guide on how to setup a Twitter bot. I’ll try to make this post simpler and complete. You’ll need access to a web server with PHP and MySQL installed for this implementation.

1. Create Twitter Account for the Bot

Create a new Twitter account and register a new app. Fill in the required name, description, and website fields. Once the app is created, go to the “Settings” tab and change “Application Type” to “Read and Write.” Save these settings. Go to the “Details” tab, scroll to the bottom, and generate your access tokens.

2. Get the PHP TwitterOAuth Library

Download the TwitterOAuth PHP library from Github. Don’t worry if you don’t know what OAuth is. Just think of it as a special key that allows your bot to operate the Twitter account. Get the latest version. Unzip the downloaded file and put the twitteroauth folder in your server.

Update

Thanks to improvements in the Twitter API, you’ll no longer have to do step 3. After you’ve created your app, simply click “Create My Access Token” at the bottom of the page. Congrats, you’ve bypassed all the steps below. Note your access token and access token secret. Now skip to step 4.

Open the twitteroauth folder. Open the file “twitteroauth.php.” Replace the function getAccessToken on line 111 with the following:

1
2
3
4
5
6
7
8
9
10
function getAccessToken($token = NULL, $pin = NULL) {
  if ($pin) {
    $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', array('oauth_verifier' => $pin));
  } else {
    $request = $this->oAuthRequest($this->accessTokenURL());
  }
  $token = OAuthUtil::parse_parameters($request);
  $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
  return $token;
}

Put the twitteroauth folder on your web server.

3. Get Access Tokens

Create a file called register.php in your server that you can access via a browser, i.e. it has be served up via something like Apache. Paste the code below into it. Replace CONSUMER_KEY and CONSUMER_SECRET with the characters Twitter gave you when you registered your app. Also replace path/to with the path to your twitteroauth folder, i.e. the location of the twitteroauth folder either as an absolute path that looks like /var/www or as a relative path like htdocs/.

1
2
3
4
5
6
7
8
9
10
11
require_once('path/to/twitteroauth/twitteroauth.php');
$oauth = new TwitterOAuth('CONSUMER_KEY','CONSUMER_SECRET');
$request = $oauth->getRequestToken();
$requestToken = $request['oauth_token'];
$requestTokenSecret = $request['oauth_token_secret'];
// place the generated request token/secret into local files
file_put_contents('request_token', $requestToken);
file_put_contents('request_token_secret', $requestTokenSecret);
// display Twitter generated registration URL
$registerURL = $oauth->getAuthorizeURL($request);
echo '<a href="' . $registerURL . '">Register with Twitter</a>';

Log into Twitter with the account you want the app to use. Access register.php via your browser. The script will create two files in its directory containing your request token and request token secret. A link will display on the screen; this is a link to your registration URL. Click the link, and Twitter will ask if you’d like to authorize the app to access your account. Click “allow.” You will see a 7-digit pin. Copy this down.

Create a file called validate.php that’s also accessible via a browser. Put this code into it and remember to replace CONSUMER_KEY and CONSUMER_SECRET and the path/to.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Retrieve our previously generated request token & secret
$requestToken = file_get_contents("request_token");
$requestTokenSecret = file_get_contents("request_token_secret");
// Include class file & create object passing request token/secret also
require_once("path/to/twitteroauth/twitteroauth.php");
$oauth = new TwitterOAuth('CONSUMER_KEY', 'CONSUMER_SECRET', $requestToken, $requestTokenSecret);
// Generate access token by providing PIN for Twitter
$request = $oauth->getAccessToken(NULL, $_GET["pin"]);
$accessToken = $request['oauth_token'];
$accessTokenSecret = $request['oauth_token_secret'];
// Save our access token/secret
file_put_contents("access_token", $accessToken);
file_put_contents("access_token_secret", $accessTokenSecret);

Access validate.php via your browser, but in the URL also pass in the PIN like so: “http://www.yoursite.com/validate.php?pin=XXXXXXX”. This will create two files called access_token and access_token_secret to the folder. If you wait a while to do this, your pin will expire. If this happens, generate a new pin by running register.php again in your browser.

4. Test Your App is Authorized

Create a file called test.php in your server with the below and replace CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_TOKEN_SECRET, and path/to.

1
2
3
4
5
6
7
8
// Create our twitter API object
require_once("path/to/twitteroauth/twitteroauth.php");
$oauth = new TwitterOAuth('CONSUMER_KEY', 'CONSUMER_SECRET', 'ACCESS_TOKEN', 'ACCESS_TOKEN_SECRET');
// Send an API request to verify credentials
$credentials = $oauth->get("account/verify_credentials");
echo "Connected as @" . $credentials->screen_name;
// Post our new "hello world" status
$oauth->post('statuses/update', array('status' => "hello world"));

Run in your browser. Check your Twitter account. You should have a new tweet that says “hello world.” You will now longer need to go through this authorization process again (unless you revoke this app’s access to this account).

5. Delete Unnecessary Files

You can now delete register.php, validate.php, request_token, request_token_secret, and test.php. Jot down the access_token and access_token_secret and delete these two files too.

6. Create Database of Tweets

I wanted a Twitter bot that tweets out Jimmy McNulty quotations from the TV series The Wire. MySQL is an open source and free database software that many hosting companies pre-install in their servers. (You can implement this with other databases like PostgreSQL and SQLite as well, but I’m not sure about the specifics.) I created a table that holds McNulty’s quotations with this SQL command:

1
2
3
4
5
6
CREATE TABLE `bot_tweets` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NOT NULL.
  `quote` VARCHAR(140) NOT NULL,
  PRIMARY KEY (id)
)

I have a name column in case I want to have multiple Twitter bots each of which is a different character tweeting different quotes. (Note how quote is restricted to 140 characters as this is the maximum number of characters allowed in a single tweet.)

Now fill up your table with the quotations you want.

7. Create the Bot Itself

This script does the actual tweeting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* database parameters */
$db_host = 'DATABASE_HOSTNAME';
$db_user = 'DATABASE_USERNAME';
$db_pw = 'DATABSE_PASSWORD';
$db = 'DATABASE_NAME';

/* Twitter keys & secrets here */
$consumer_key = 'CONSUMER_KEY';
$consumer_secret = 'CONSUMER_SECRET';
$access_token = 'ACCESS_TOKEN';
$access_token_secret = 'ACCESS_TOKEN_SECRET';

/* Twitter keys & secrets here */
$query = 'YOUR_QUERY';
$name = 'CHARACTER';
$file_name = 'NAME_OF_FILE.log';

function get_rand_quote($name){
  $result = mysql_query("SELECT * FROM bot_tweets WHERE name = '$name' ORDER BY RAND() LIMIT 1");
  $row = mysql_fetch_array($result);
  $quote = $row['quote'];
  return $quote;
}

// connect to mySQL database
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');

// Create Twitter API object
require_once('path/to/lib/twitteroauth.php');
// get access token and secret from Twitter
$oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret);
// fake a user agent to have higher rate limit
$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';

// Send an API request to verify credentials
$credentials = $oauth->get('account/verify_credentials');
echo 'Connected as @' . $credentials->screen_name . '\n';

// Show API hits remaining
$remaining = $oauth->get('account/rate_limit_status');
echo "Current API hits remaining: {$remaining->remaining_hits}.\n";

$since_id = file_get_contents($file_name);
// search Twitter's REST API for tweets containing words in $query
// and more recent than specified tweet_id
$tweets_found = $oauth->get('http://search.twitter.com/search.json',
                            array('q' => $query, 'since_id' => $since_id))->results;
// if a more recent tweet has appeared, store this tweet's id in the file
if (isset($tweets_found[0])) {
  file_put_contents($file_name, $tweets_found[0]->id_str);
}
print_r($tweets_found);

foreach ($tweets_found as $tweet){
  $quote = get_rand_quote($name);
  $user = '@' . $tweet->from_user;
  echo "$user $quote\n\n";
  $oauth->post('statuses/update', array('status' => "$user $quote"));
  sleep(10);
}

Fill in the appropriate values for your MySQL database’s host, username, password, and database name. Also fill in your access_token, access_token_secret, consumer_key, and consumer_secret. Now schedule a cron job to run this script at a specified interval. For security reasons, don’t put this file in a place where it can be seen via a browser. Otherwise anyone can screw with your Twitter account because they know your access tokens.

This script search for tweets that mention the words you put in $query. It will then at-reply those users with a random quote for each. The next time it runs, the script searches for tweets that occurred after the most recent tweet it at-replied last time it ran. It stores the most recent tweet it found last time in a separate file that you’ll name in the variable $file_name.

8. Room for Improvements

This implementation can be close to real time if you set the interval between each time the script runs to be very short, e.g. one minute. My hosting provider is GoDaddy. They’re good for domain name registration but not for hosting. I’ll be switching when my subscription expires next year. They aren’t good hosts because they constrain the type of technical features I can use. I can’t schedule a task to run at intervals shorter than half-an-hour.

I also cannot figure out with this current PHP library how to search for an exact phrase. I want only tweets that have the exact phrase “the wire.” The current script returns tweets that have both words not necessarily together. I’ve tried these values for $querybut none of them work: '"the wire"' "'the wire'" '"the wire"'. If you figure out a way, please post the solution below.

Twitter has a streaming API that allows you to essentially stick your mouth up to a customized firehose of tweets. This allows our bot to respond in real time. You’ll need the Phirehose PHP library here. Unfortunately, GoDaddy won’t let me implement this because its servers kill long-running processes.

9. Have Some Fun

Check out my Jimmy McNulty, Don Draper, and Jack Bauer bots.