A Retweeting Twitterbot in Perl


Image representing Twitter as depicted in Crun...

Image via CrunchBase

I'm trying an experiment with this year's Utah legislative session, I've created a Twitter account (@utahpolitics) and set up an autofollower on it (hat tip to @jesse). I wanted to also set up a retweeting twitterbot so that people following the account would see what anyone else following the account said when it contained certain keywords.

The world probably doesn't need yet another retweeter, but I couldn't find exactly what I was looking for and decided to build one for a few reasons:

  1. I like to program
  2. I want to understand the Twitter API more deeply
  3. I didn't want to modify someone's PHP or Python code
  4. Oh, and I like to program

Armed with those few requirements and Chris Thompson's Net::Twitter library, I wrote the following program in an hour or so:

#!/usr/bin/perl -w
use strict;

use Net::Twitter;
use DateTime;
use DateTime::Format::HTTP;
use Fcntl;
use SDBM_File;
use File::Copy;

my $dt = DateTime->now;
$dt->subtract( minutes => 60); 

my $class = 'DateTime::Format::HTTP';
my $since = $class->format_datetime($dt);

my $today_string = $dt->strftime("%M");
my $seen_dir = "./seen";
my $seen_file = "$seen_dir/latest.db";
my $backup_file = "$seen_dir/$today_string.db";

# make a backup of the hash and open it
unless(-e "$backup_file.pag") {
    copy("$seen_file.pag","$backup_file.pag");
    copy("$seen_file.dir","$backup_file.dir");
}

my %seen;
tie %seen, "SDBM_File", $seen_file, O_CREAT|O_RDWR, 0644 || 
  die "Can't link to $seen_file, $!\
";

# set your own username and password here
my $user = 'put_your_twitter_screenname_here';
my $password = 'put_your_password_here';

my $twit = Net::Twitter->new(
   username=>$user, 
   password=>$password, 
   source => "Utah Politics Retweeter",
   clientname=>"UtahPolitics ReTweeter");

# find replies
my $retweets = [];
my $twit_replies = 
     $twit->friends_timeline({since => $since, count=>100}) ;

foreach my $reply (@{ $twit_replies }) {
  my $text = $reply->{'text'};
  my $id = $reply->{'id'};
  my $name = $reply->{'user'}->{'screen_name'};
  print ".";
  if($text =~ m/utahpolitics|#utpolitics/  &&
     ! $seen{$id} 
    ){
        unshift @{ $retweets}, 
\t        {'name' => $name,
\t         'id' => $id,
\t         'text' => $text
\t } unless $name eq $user;
  }
}

print "\
";

foreach my $retweet (@{ $retweets }) {
  print ".";
  my $status = "(@".$retweet->{'name'}.") ".
               $retweet->{'text'};
  my $code = $twit->update($status);
  $seen{$retweet->{'id'}} = 1 if $code;
}

print "\
";

1;

I made heavy use of Data::Dumper to Dump data structures I got back from the library during development. This could be generalized in lots of ways. For example, passing the username and password along with the keywords to look for as arguments would allow it to be used for more than one ID. I run this as a cronjob every five minutes and so far it seems to be working fine.