The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Plagger::Plugin::Publish::2chdat;
use strict;
use base qw( Plagger::Plugin );

use Encode;
use File::Spec;

sub register {
    my($self, $context) = @_;
    $context->register_hook(
        $self,
        'publish.feed' => \&feed,
        'publish.finalize' => \&finalize,
    );
}

sub init {
    my $self = shift;
    $self->SUPER::init(@_);

    my $dir = $self->conf->{dir};
    unless (-e $dir && -d _) {
        mkdir $dir, 0755 or Plagger->context->error("$dir: $!");
        mkdir File::Spec->catfile($dir, 'dat'), 0755 or Plagger->context->error("$dir/dat: $!");
    }
}

sub feed {
    my($self, $context, $args) = @_;

    my $feed = $args->{feed};
    my $out  = File::Spec->catfile($self->conf->{dir}, 'dat', $feed->id_safe . ".dat");
    $context->log(info => "Writing dat output to $out");

    my $anonymous = decode("utf-8", $self->conf->{default_anonymous} || "名無しさん");

    open my $fh, ">:encoding(shift_jis)", $out or $context->error("$out: $!");
    printf $fh "%s<><>%s ID:%s<> %s<>%s\n",
        ($feed->author || $feed->entries->[0]->author || $anonymous),
        $self->format_date( Plagger::Date->from_epoch(0) ), # Fix created date to handle bytes-range request
        substr($feed->id_safe), 0, 8),
        $self->format_body($feed->description) . "<BR>" . $feed->link,
        $feed->title;

    for my $entry (reverse $feed->entries) {
        printf $fh "%s<><>%s ID:%s<> %s\n",
            ($entry->author || $anonymous),
            $self->format_date( $entry->date || Plagger::Date->now ),
            substr($entry->id_safe, 0, 8),
            $self->format_body($entry->body) . "<P>" . $entry->link . "</P>";
    }

    close $fh;

    # update mtime of file with the latest entry
    if (my $mtime = $feed->entries->[0]->date) {
        utime $mtime->epoch, $mtime->epoch, $out;
    }
}

sub finalize {
    my($self, $context, $args) = @_;

    my $out = File::Spec->catfile($self->conf->{dir}, 'subject.txt');
    open my $fh, ">:encoding(shift_jis)", $out or $context->erorr("$out: $!");
    for my $feed ($context->update->feeds) {
        printf $fh "%s.dat<>%s (%d)\n", $feed->id_safe, $feed->title, $feed->count;
    }
}

sub format_date {
    my($self, $date) = @_;
    my $clone = $date->clone;
    $clone->set_locale("ja_JP");
    return $clone->strftime("%Y/%m/%d(%a) %H:%M:%S");
}

sub format_body {
    my($self, $body) = @_;

    # replace images
    $body =~ s!<img .*?src="(.*?)".*?>!$1!ig;

    # ad-hoc replacement of A links
    $body =~ s{<a .*?href="(.*?)".*?>(.*?)</a>}
              {my($url, $label) = ($1, $2);
               $label =~ m!^https?://! ? $url : "$label ($url)"}ieg;

    # respect newline in <pre> tags
    $body =~ s{(<pre>)(.*?)(</pre>)}{$1 . nl2br($2) . $3}seig;

    # other than that, nuke newlines
    $body =~ tr/\r\n//d;

    $body;
}

sub nl2br {
    my $str = shift;
    $str =~ s/\r?\n/<BR>/g;
    $str;
}

1;

__END__

=head1

Plagger::Plugin::Publish::2chdat - Publish 2ch data files for 2ch browsers

=head1 SYNOPSYS

  - module: Publish::2chdat
    config:
      dir: /home/miyagawa/public_html/2chdat

=head1 DESCRIPTION

This plugin publishes feed updates as 2ch browser compatible data
files e.g. C<subject.txt> and C<*.dat> files.

Note that this plugin might not work very well with Subscription
plugins that manages unread management, e.g. Bloglines and Livedoor
Reader.

=head1 CONFIG

=head2 dir

Directory to save data files in.

=head1 AUTHOR

Tatsuhiko Miyagawa

=head1 SEE ALSO

L<Plagger>, L<http://age.s22.xrea.com/talk2ch/>

=cut