My First Twitter App

Today I finished my first twitter app. My main point in posting this entry is to talk about how wonderful is Perl, and in particular how fancy is Net::Twitter. If you already know these things you can safely stop reading now.

My completed application is two programs each of which appear to be 67 lines long. In addition to providing a feed into Twitter I also threw in a CGI component, just for giggles.

Including registering the app it took about three hours, most of which was spent misreading the Net::OAuth and Net::Twitter::OAuth docs. Here's the short version of my storyIf you are targeting twitter don't bother with either of those.. Everything you need is done though a Net::Twitter instance; however, there's a bit of documentation you'll want hiding over in the pod for Net::Twitter::Role::OAuth. Longer version -with complete programs- follow.

As some may know, I'm the son of New York Times Best-selling Grampa Steven Brust. The app I was charged to create generates a Quote Of the Day to be sent from @Dragaera - a twitter account shamelessly promoting his bread-winning universe, pushing schwag and so on.

This was an easy project in all respects.

  • There's only one required function: to post a random quote;
  • There's only one twitter account involved; and,
  • The data was just sitting around waiting for me.

I did end up adding one addition feature. Twitter applications are required to list an "Application Website" This is where one would go to download a stand-alone application or where you go to interact with one that's web-based. Since I didn't really have anything interesting to do with that, but needed to give an URL, I decided visitors to that location should also receive a random quote for their trouble.

The first program is the one which took 2.5 of my three hour investment. The purpose of this program is to managing the process of getting the application permissions on behalf on a users. Technically, this will -eventually- create an Access Token and an Access Token Secret and store them in a file. (Have a look at Twitter's Authorization Docs or the more general OAuth Terminology Document if the gears and cogs of this are what you are after.) As an aside, my major malfunction turned out to running perldoc on a different system - one with an out-dated version of Net::Twitter. This especially was painful because example code from older versions look quitesimilar to the present heuristic - but don't quite work.

Once I got back on the right (manual)page, I found the instructions from the current documentation work just fine. This first program (oauth.pl) just for the basically fills in the blanks from the first example in the Net::Twitter::Role::OAuth man page.

oauth.pl
use Net::Twitter;

our $consumer_key    = q(things);
our $consumer_secret = q(andStuff);

my $nt = Net::Twitter->new
  (
   traits          => ['API::REST', 'OAuth'],
   consumer_key    => $consumer_key,
   consumer_secret => $consumer_secret,
  );

# You'll save the token and secret in cookie, config file or session database
my($access_token, $access_token_secret) = restore_tokens();
if ($access_token && $access_token_secret)
  {
    $nt->access_token($access_token);
    $nt->access_token_secret($access_token_secret);
  }

unless ( $nt->authorized )
  {
    # The client is not yet authorized: Do it now
    print "Authorize this app at ", $nt->get_authorization_url, " and enter the PIN#\n";

    my $pin = <STDIN>; # wait for input
    chomp $pin;

    my($access_token, $access_token_secret, $user_id, $screen_name) = $nt->request_access_token(verifier => $pin);
    save_tokens($access_token, $access_token_secret); # if necessary
  }

print "Done\n";
exit( 0 );

sub restore_tokens
  {
    open my $fh, '<', ".oauth" or die qq(failed to create file; $!);
    my($access_token, $access_token_secret) = map
      {
        chomp;
        s/^[^:]+:// if defined and length;
        $_
      } <$fh>;
    close $fh;
    return ($access_token, $access_token_secret);
  }

sub save_tokens
  {
    die qq(Not enough arguments to save_tokens) unless @_ > 1;
    my($access_token, $access_token_secret) = @_;
    die qq(Access token is required) unless $access_token;
    die qq(Access token secret is required) unless $access_token_secret;
    open my $fh, '>', ".oauth" or die qq(failed to create file; $!);
    my $ofh = select $fh;
    print "token:$access_token\n";
    print "secret:$access_token_secret\n";
    select $ofh;
    close $fh;
  }

__END__

The other program does business. It connects to twitter, presents credentials verifying it was authorized by a particular user/account, selects a quote at random and posts it. You might think this would be more interesting but, and such is the beauty of Net::Twitter, CPAN, and perlspace in general, it's even shorter -notwithstanding a DATA block at EOF.

index.pl
#!/usr/bin/perl

use strict q(vars);
use warnings;

use Net::Twitter;

our $consumer_key          = q(stuff);
our $consumer_secret       = q(andThings);
our $token;
our $token_secret;

sub get_quote {
  my (@quotes) = grep { length() < 141 } map { chomp; $_ } <DATA>;
  return $quotes[rand @quotes];
}

sub restore_tokens {
  open my $fh, '<', ".oauth" or die qq(failed to create file; $!);
  my($access_token, $access_token_secret) = map
  {
    chomp;
    s/^[^:]+:// if defined and length;
    $_
  } <$fh>;
  close $fh;
  return ($access_token, $access_token_secret);
}

# just show a quote and be done if we're called from a browser
if (exists $ENV{QUERY_STRING}) {
  eval "use CGI";
  my $q = new CGI;
  print $q->header;
  print $q->h1( "Random Steven Brust Quote" );
  print $q->h2( get_quote);
  exit( 0 );
}

($token,$token_secret) = (restore_tokens);
die "Not Authorized" unless $token && $token_secret;

my $nt = Net::Twitter->new
(
 traits   => [qw/OAuth API::REST/],
 consumer_key        => $consumer_key,
 consumer_secret     => $consumer_secret,
 access_token        => $token,
 access_token_secret => $token_secret,
);

my $result = $nt->update( get_quote );
#use Data::Dumper; print Dumper( $result );
exit( 0 );

__DATA__
Maybe it's just me, but... there's nothing like having someone try to kill you to take your mind off your problems.
No matter how subtle the wizard, a knife between the shoulder blades will seriously cramp his style.
...

So, in conclusion: it sure is fun to code stuff for perl! There's nothing more satisfying that discovering that you can crank out an interface to an 3rd party API you've never worked with before in just a few hours. Or if there is, it's knowing you've only adding about 100 lines to the bulk of the code you support.

8 Comments

Glad it worked out for you. Since Net::OAuth didn't do what you needed, perhaps add a review for it on CPAN Ratings?

I know little of Steven Brust's work, aside from To Reign in Hell and I must confess that I was very impressed with it, though I suspect those who know me know exactly why I liked it.

Happy to hear that you're loving Perl!

You should remove the line

use lib "$ENV{HOME}/perl5/lib/perl5";

from index.pl file.

It can annoy people playing with local::lib and perlbrew not finding the correct Net::Twitter after installation O:-)

oauth.pl that is

Hi there,

Impressive stuff. I would like to reuse some of it for my own purposes, but I can't get it to work. It keeps complaining that files don't exist. how do I get that sorted? (my $fh etc).

Thanks

Willem

@Willem (or, more likely, anyone that stumbles across this post later) - the program assumes that the .oauth file exists on the first run. Just create a file with that name in the directory.

I'm just coming across this now. I'm wondering if there are any problems I should anticipate given that the original post was written a few years ago. Maybe it's a little bit of laziness on my part, and apologize for that but maybe a confirmation at this point would be useful to others who come along in a week, or a year.

Thanks for the work and taking the time to share. Cheers.

Leave a comment

About corwin

user-pic Perl. Yes, have some.