using Tumblr API v2 from Perl

Tumblr API v2 uses oAuth mechanism for authentication, and this mechanism signs every request with a SHA1 or other hash algorithms.

If your posts contain non-ASCII data, here starts the tricky part. Perl has some special treatment for UTF-8 strings, and incorrect handling can easily lead to a wrong signature.

At first, I tried to use LWP::Authen::OAuth module, as it provides the simplest interface and hides most of oAuth internal logic. Unfortunately, it doesn’t care much about the stging encodings, and UTF8-formatted strings get corrupted and result in invalid signatures.

Net::OAuth appeared to be a bit more complex, and a bit insufficiently documented, but proved to work correctly with UTF-8 data.

Here’s the piece which finally worked. It took awhile to build this example, as the module documentation cuts some important corners.

use utf8;
use Net::OAuth;
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
use HTTP::Request::Common;
use LWP::UserAgent;
use strict;

my $blog = 'example.tumblr.com';

# token and token_secret are previously retrieved via authorization request
my %oauth_api_params =
    ('consumer_key' =>
        'Tte6Jw...............',
     'consumer_secret' =>
        'H6z7...................',
     'token' =>
        'XPcrob...........',
     'token_secret' =>
        'Wz...................',
     'signature_method' =>
        'HMAC-SHA1',
     request_method => 'POST',
);

my $body =
   '<div style="text-align:center">Hello wörld</div>';

# prepare the body for further encode
utf8::decode($body);

my $url = 'http://api.tumblr.com/v2/blog/' . $blog . '/post';

my $request =
    Net::OAuth->request("protected resource")->new
        (request_url => $url,
         %oauth_api_params,
         timestamp => time(),
         nonce => rand(1000000),
         extra_params => {
             'type' => 'text',
             'body' => $body,
         });

$request->sign;

my $ua = LWP::UserAgent->new;
# this is the tricky part which is not documented where it should
my $response = $ua->request(POST $url, Content => $request->to_post_body);

if ( $response->is_success )
{
    my $r = decode_json($response->content);
    if($r->{'meta'}{'status'} == 201)
    {
        my $item_id = $r->{'response'}{'id'};
        print("Added a Tumblr entry\n");
    }
    else
    {
        printf("Cannot create Tumblr entry: %s\n",
                $r->{'meta'}{'msg'});
    }            
}
else
{
    printf("Cannot create Tumblr entry: %s\n",
            $response->as_string);
}
Advertisements

, ,

  1. #1 by Shadow Blaze on July 22, 2012 - 9:27 pm

    How do I get token and token_secret for the oauth_api_params hash? You said “previously retrieved via authorization request”, but that doesn’t really help much.

    • #2 by txlab on July 22, 2012 - 9:32 pm

      sorry, I don’t remember. I guess it was easy. It was a one-time implementation, and it works, and I never touched this subject again

    • #3 by Ryan Wark on August 5, 2012 - 3:30 pm

      Hi Shadow,

      I was having the same problem you were. Getting the ‘tokens’ is part of the OAuth ‘workflow’ and something that does have to be done separately. I’ve written more information about it as well as provided a helpful Perl script to get the access tokens. *insert shameless plug here* http://ryanwark.com/blog/posting-to-the-tumblr-v2-api-in-perl

  2. #4 by kidstrangelove on May 15, 2013 - 8:27 pm

    Hey, I’m curious – have you ever gotten a local file upload working? I’m trying to create a ‘photo’ post, but my image source is local, so I have to use the “data’ parameter. The tumblr API page does a poor job saying exactly what they want in the data parameter

    • #5 by txlab on May 15, 2013 - 8:33 pm

      nope, didn’t try it. My script started to work as I wanted, and I never returned to that topic.

  3. #6 by Marek Foss (@f055) on September 9, 2014 - 11:32 am

    FYI: I was using WWW::Tumblr and stumbled upon the same problem – posting via API crashed every time I was trying to send wide-character text, like UTF pictograms or Russian Cyrillic characters. I found a great and simple solution: since Tumblr API by default accepts post data as HTML, I used HTML::Entities to encode non-ascii UTF chars to &XXX; entities before sending via the API – worked like a charm.

  4. #7 by Nathanaelle on July 16, 2016 - 7:48 pm

    Is it okay to use this for a Tumblr bot?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: