From ea5d5fe4d5617326ec61c26157bca886b8614435 Mon Sep 17 00:00:00 2001 From: dakkar Date: Sun, 9 Dec 2012 15:16:41 +0000 Subject: maildir publisher --- lib/Feed.pm | 13 ++++ lib/Feed/Mail.pm | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/Feed/MailDir.pm | 76 ++++++++++++++++++++++ lib/Feed/Printer.pm | 2 +- 4 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 lib/Feed/Mail.pm create mode 100644 lib/Feed/MailDir.pm (limited to 'lib') diff --git a/lib/Feed.pm b/lib/Feed.pm index fbcdf10..652c438 100644 --- a/lib/Feed.pm +++ b/lib/Feed.pm @@ -30,6 +30,19 @@ has feed => ( builder => 'get_feed', ); +has title => ( + is => 'ro', + isa => 'Str', + lazy_build => 1, + builder => 'extract_title', +); + +sub extract_title { + my ($self) = @_; + + return $self->feed->title; +} + has _entries => ( is => 'ro', isa => 'ArrayRef[XML::Feed::Entry]', diff --git a/lib/Feed/Mail.pm b/lib/Feed/Mail.pm new file mode 100644 index 0000000..cc52b6a --- /dev/null +++ b/lib/Feed/Mail.pm @@ -0,0 +1,179 @@ +package Feed::Mail; +use Moose::Role; +use 5.016; +use namespace::autoclean; +use Encode; +use Encode::MIME::Header; +use MIME::Lite; +use DateTime::Format::Mail; +use Digest::SHA1 qw/ sha1_hex /; +use Template; +use Template::Provider::Encoding; +use Template::Stash::ForceUTF8; +use Moose::Util::TypeConstraints; + +subtype 'ArrayOfStr', as 'ArrayRef[Str]'; +coerce 'ArrayOfStr', from 'Str', via { [ $_ ] }; + +has _mail_folders => ( + is => 'ro', + isa => 'ArrayOfStr', + coerce => 1, + default => sub { [] }, + traits => [ 'Array' ], + init_arg => 'mail_folders', + handles => { + mail_folders => 'elements', + } +); + +has tt => ( + is => 'ro', + isa => 'Template', + lazy_build => 1, +); + +has template => ( + is => 'ro', + isa => 'Str', + lazy_build => 1, +); + +has date_formatter => ( + is => 'ro', + lazy_build => 1, +); + +sub _build_tt { + my ($self) = @_; + + return Template->new( + LOAD_TEMPLATES => [ Template::Provider::Encoding->new ], + STASH => Template::Stash::ForceUTF8->new, + ); +} + +sub _build_template { + my ($self) = @_; + + my $curpos = tell(DATA); + binmode(DATA); + my $template = do {local $/;}; + seek(DATA,$curpos,0); + + return $template; +} + +sub _build_date_formatter { + return DateTime::Format::Mail->new(); +} + +sub entry_to_mime { + my ($self,$entry) = @_; + + my $from = 'feeder@localhost'; + my $date = $entry->modified; + my $from_name = $entry->author // $self->title; + $from_name =~ tr/,//d; + my $id = sha1_hex($entry->id); + + my $body = $self->prepare_body($entry); + + my $msg = MIME::Lite->new( + Date => $self->date_formatter->format_datetime($entry->modified), + From => encode('MIME-Header',qq{"$from_name" <$from>}), + To => $from, + Subject => encode('MIME-Header',$entry->title//'(no title)'), + Type => 'multipart/related', + ); + $msg->attach( + Type => 'text/html; charset=utf-8', + Data => encode('utf-8',$body), + Encoding => 'quoted-printable', + ); + + $msg->add('Message-Id', "<$id.feeder\@localhost>"); + $msg->add('X-Tags', encode('MIME-Header', join(' ', $entry->tags ))); + + return $msg; +} + +sub prepare_body { + my ($self,$entry) = @_; + + my $template=$self->template; + my $out; + $self->tt->process( + \$template, + { + feeder => $self, + feed => $self->feed, + entry => $entry, + content => $entry->content, + }, + \$out, + ) + or die $self->tt->error; + return $out; +} + +1; + +__DATA__ +[% USE encoding 'utf-8' -%] + + + + + + + +
+ [% SET link = entry.link || entry.id -%] + [% entry.title %]
+ [% feed.title %]
+ [% IF entry.author %] + by [% entry.author | html %] + [% END %] + [% IF entry.tags.size %] + on [% entry.tags.join(',') %] + [% END %] +
+
+ [% IF content.body -%] + [% IF content.body.match('(?i)^]') %] + [% content.body %] + [% ELSE %] +
[% content.body %]
+ [% END %] + [% ELSE %] +
+ [% END %] +
+
+ [% IF entry.issued %] + Posted on [% self.date_formatter.format_datetime(entry.issued) %] + [% END %] + [% IF entry.modified %] + Modified on [% self.date_formatter.format_datetime(entry.modified) %] + [% END %] + | + permalink + | + [% feed.title | html %] +
+
+ + diff --git a/lib/Feed/MailDir.pm b/lib/Feed/MailDir.pm new file mode 100644 index 0000000..4b8f542 --- /dev/null +++ b/lib/Feed/MailDir.pm @@ -0,0 +1,76 @@ +package Feed::MailDir; +use Moose::Role; +use 5.016; +use namespace::autoclean; +use Encode; +use Encode::IMAPUTF7; +use MooseX::Types::Path::Class 'Dir'; +use Maildir::Lite; + +with 'Feed::Mail'; + +requires 'extract_entries'; + +has maildir_base => ( + is => 'ro', + isa => Dir, + coerce => 1, + required => 1, +); + +has maildir_folder => ( + is => 'ro', + isa => 'Maildir::Lite', + lazy_build => 1, +); + +sub _build_maildir_folder { + my ($self) = @_; + + my $feed = $self->feed; + + my $folder = encode('IMAP-UTF-7',$self->maildir_folder_for($feed)); + + $self->maildir_base->mkpath; + + return Maildir::Lite->new( + dir => $self->maildir_base->subdir($folder)->stringify, + ); +} + +sub process_entry { + my ($self,$entry) = @_; + + my ($msg) = $self->entry_to_mime($entry); + + $self->store_into_maildir($msg); + + return; +} + +sub _clean_folder_part { + return map { s{\W+}{-}gr } @_; +} + +sub maildir_folder_for { + my ($self,$feed) = @_; + + my (@comps) = _clean_folder_part($self->mail_folders,$self->title); + return join '.','',@comps; +} + +sub store_into_maildir { + my ($self,$msg) = @_; + + my ($fh,$stat) = $self->maildir_folder->creat_message(); + die "maildir create message failed ($stat)" if $stat; + + binmode($fh); + $msg->print($fh) + or die "print to maildir failed: $!"; + + $self->maildir_folder->deliver_message($fh) + and die "maildir write failed"; +} + +1; diff --git a/lib/Feed/Printer.pm b/lib/Feed/Printer.pm index 89dd93f..8d673e0 100644 --- a/lib/Feed/Printer.pm +++ b/lib/Feed/Printer.pm @@ -8,7 +8,7 @@ requires 'process'; before process => sub { my ($self) = @_; - say $self->feed->title; + say $self->title; }; sub process_entry { -- cgit v1.2.3