#!/usr/bin/perl -w # friendsonly.pl - decklin@red-bean.com # # for stacy. as the title implies, it makes all public entries in your # journal friends-only. i did not actually test this on my entire # journal, only segments (since the conversion is sort of one-way; you # can't differentiate between entries that were always friends-only # and newly protected entries once the script has been run). i tried # it on months that had all public entries, though. # # i might as well write the disclaimer now, because this is going to # be distributed whether i like it or not... # # WARNING: THIS EDITS EVERY SINGLE PUBLIC ENTRY IN YOUR JOURNAL. IF # THE CODE IS BUGGY (AND IT MAY WELL BE), YOU COULD LOSE THOSE ENTRIES # PERMANENTLY. IF THIS WRECKS YOUR JOURNAL SOMEHOW, I'M SORRY, BUT I # CLAIM NO RESPONSIBILITY FOR THE USE OF THIS PROGRAM. # # ok then, on with the show. use LiveJournal; $|++; # change this, of course my %info = ( user => 'yourname', password => 'yourpass', ); # if you make this smaller, it is not my fault when the LJ admins ban # your IP. if you can stand making it bigger, please do. my $delay = 60; # yes, the main program is mercifully short. my $lj = LiveJournal::Journal->new(\%info); my $session = $lj->login(); assert_lj_response($session, 'logging in'); my $days = $lj->getdaycounts(); assert_lj_response($days, 'getting daily entry counts'); while (my ($date, $count) = each (%$days)) { if ($date =~ /(\d+)-(\d+)-(\d+)/) { # uncomment this line to only modify a given date or whatever. # next unless ($1 == 2001 && $2 == 12 && $3 == 31); print "Modifying $count entries for $date: "; foreach_entry_in_day($1, $2, $3, \&make_friends_only); print "done.\n"; } } # i will resist the temptation to give this function a name involving 'map'. sub foreach_entry_in_day { my ($year, $month, $day, $action) = @_; my %getinfo = ( selecttype => 'day', year => $year, month => $month, day => $day, ); my $items = $lj->get(\%getinfo); assert_lj_response($items, "fetching list of entries for $year-$month-$day"); $events = parse_lj_events($items); while (my ($itemid, $event) = each (%$events)) { &$action($itemid, $event); } } # this could be a bit more flexible. sub make_friends_only { my ($itemid, $event) = @_; if (!defined $event->{security} || $event->{security} eq 'public') { $event->{security} = 'usemask'; $event->{allowmask} = 1; } my $resp = $lj->edit($event); assert_lj_response($resp, "editing entry $itemid"); print "$itemid "; sleep($delay); } # in: ugly hashref returned by getitems mode # out: hashref keyed by itemid to hashrefs suitable for editevents sub parse_lj_events { my ($raw_items) = @_; my (%parsed_items, %events); for my $item (keys %$raw_items) { if ($item =~ /(\w+)_(\d+)_(.*)/) { $parsed_items{$1}[$2 - 1]{$3} = $raw_items->{$item}; } } # bloody hell, why can't the server parse the date? for my $item (@{$parsed_items{events}}) { for my $prop (keys %$item) { if ($prop eq 'eventtime' && $item->{$prop} =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/) { $events{$item->{itemid}}->{eventtime} = $item->{$prop}; $events{$item->{itemid}}->{year} = $1; $events{$item->{itemid}}->{mon} = $2; $events{$item->{itemid}}->{day} = $3; $events{$item->{itemid}}->{hour} = $4; $events{$item->{itemid}}->{min} = $5; $events{$item->{itemid}}->{sec} = $6; } else { # i'm not exactly sure what the value of sending back # only the event text cooked is. oh well, hooray for # special cases ... $events{$item->{itemid}}->{$prop} = $prop eq 'event' ? $item->{$prop} : $lj->escape($item->{$prop}); } } } # i never met a parsed item i didn't like. for my $meta (@{$parsed_items{prop}}) { $events{$meta->{itemid}}->{"prop_$meta->{name}"} = $lj->escape($meta->{value}); } return \%events; } # i really should have thought this up earlier. sub assert_lj_response { my ($res, $what) = @_; unless (defined $res->{success} && $res->{success} eq "OK") { my $error = $res->{errmsg} || 'unknown'; die "LiveJournal error ($what): $error"; } }