#!/usr/bin/perl -w # Copyright Dmitri Vassilenko (dv [at] glyphy [dot] com) # Personal use is OK. Keep the ownership info. # Needs to be called from cron (or whatever you have) once in a while. # Upon each successive call with no arguments, it will query del.icio.us # for new posts under a certain tag and store the fetched data on disk along # with the last post time. Naturally, you shouldn't run this in a tight loop. # I run this a couple of times a day. You may get throttled by del.icio.us if # you poll too often. # To force a post, use the -u cmd-line option. # If you post a lot, you may wish to append "&count=n" to $DEL_API, where n # is the number of posts you'd like to fetch. The default is 15 and max is 100. ### Requirements use strict; use Getopt::Std; use Data::Dumper; use LWP::UserAgent; use XML::Simple; use Storable; use DateTime::Format::W3CDTF; use Mail::Send; ########## Define constants ########## use constant LJ_USERNAME => 'YourLJUsername'; use constant LJ_PIN => 'YourLJEmailPostingPin'; my $DEL_USERNAME = 'YourDeliciousUsername'; my $DEL_PASSWORD = 'YourDeliciousPassword'; my $DEL_TAG = 'postingTag'; # changing the following is optional my $DEL_API = "https://api.del.icio.us/v1/posts/recent?tag=$DEL_TAG"; use constant TIME_FILE => '/home/you/last'; # stores last update time use constant POSTS_FILE => '/home/you/posts'; # stores queued items for posting use constant MAX_DOC_SIZE => 100000; # in bytes use constant TIMEOUT => 10; use constant MAX_POSTS => 7; # after this # of new items, an lj-cut is used use constant MIN_POSTS => 3; # don't post less than this # of items ###################################### sub usage { print "Usage: ljdelpost [-u] [-s] [-t n] Fetches the last few posts from del.icio.us and stores them on disk. -u updates and forces a post -s shows the latest posts currently stored on disk without posting them -t n trims the posts list to n last posts\n"; } # Parse commands my $cmds = getopts('usht:'); our($opt_h, $opt_s, $opt_u, $opt_t); if (defined $opt_h) { usage(); exit 0 } ########## Fetch stored data from disk ########## if (! -e TIME_FILE) { my $initial_time = 0; store(\$initial_time, TIME_FILE); } my $last = retrieve(TIME_FILE); # Last update time from epoch if (! -e POSTS_FILE) { my @initial_posts; store(\@initial_posts, POSTS_FILE); } my $sposts = retrieve(POSTS_FILE); # Posts stored on disk ################################################# if (defined $opt_t) { splice(@{$sposts}, $opt_t); store($sposts, POSTS_FILE) or die "Can't store %a in " . POSTS_FILE . "\n"; $opt_s = 1; } if (defined $opt_s) { print "Stored posts:\n"; print Dumper($sposts); exit 0; } my $ua = new LWP::UserAgent; my $content; $ua->max_size(MAX_DOC_SIZE); $ua->timeout(TIMEOUT); $ua->agent("LJDelPost (http://www.livejournal.com/users/troworld/162341.html)"); my $req = new HTTP::Request GET => $DEL_API; $req->authorization_basic($DEL_USERNAME, $DEL_PASSWORD); my $res; while ($res = $ua->request($req)) { if ($res->is_success) { $content = $res->content; last; } else { warn "Error: " . $res->code . ' ' . $res->message; exit 1 if ($res->code == 503 || $res->code == 401); # Back off sleep 60; } } my $posts = XMLin($content, ForceArray => 1)->{post}; my $f = DateTime::Format::W3CDTF->new; my $dt; # Date of $i'th post my @newposts; # will be copied to disk when complete my $newtime = $f->parse_datetime($posts->[0]->{time}); # date of last new post foreach my $i (@{$posts}) { $dt = $f->parse_datetime($i->{time}); last unless ($dt->epoch() > ${$last}); # Posts are in descending order sorted # by time push @newposts, $i; } # Copy new posts to disk if ($#newposts >= 0) { unshift @{$sposts}, @newposts; store($sposts, POSTS_FILE) or die "Can't store %a in " . POSTS_FILE . "\n"; store(\$newtime->epoch(), TIME_FILE) or die "Can't store %a in " . TIME_FILE . "\n"; # update last post time } if (defined $opt_u) { if ($#{$sposts} < 0 || $#{$sposts} < MIN_POSTS) { warn 'Not enough new posts. Not sending any messages.'; exit 0; } my $msg = new Mail::Send; $msg->to(LJ_USERNAME . '+' . LJ_PIN . '@post.livejournal.com'); #$msg->cc('your@email.com'); # CC yourself $msg->subject('Links for ' . $newtime->day . ' ' . $newtime->month_name . ' ' . $newtime->year); my $mailer = $msg->open; #open(my $mailer, '>-'); # for debugging purposes print $mailer "lj-security: public\nlj-tags: links\nlj-userpic: default\n\n"; print $mailer "" if ($#{$sposts}+1 > MAX_POSTS); print $mailer "\n\n"; print $mailer ''; print $mailer "\n" if ($#{$sposts}+1 > MAX_POSTS); $mailer->close; # Send the message my @cleanposts; store(\@cleanposts, POSTS_FILE) or die "Can't store %a in " . POSTS_FILE . "\n"; }