App-Lastmsg
view release on metacpan or search on metacpan
lib/App/Lastmsg.pm view on Meta::CPAN
package App::Lastmsg;
use 5.014000;
use strict;
use warnings;
use Config::Auto;
$Config::Auto::DisablePerl = 1;
use Date::Parse;
use Email::Folder;
use List::Util qw/max/;
use POSIX qw/strftime/;
our $OUTPUT_FILEHANDLE = \*STDOUT;
our $VERSION = '0.002001';
our @DEFAULT_INBOX;
push @DEFAULT_INBOX, "/var/mail/$ENV{USER}" if exists $ENV{USER};
push @DEFAULT_INBOX, "$ENV{HOME}/Maildir" if exists $ENV{HOME};
sub format_time { strftime '%c', localtime shift }
sub run {
my $config = Config::Auto->new(format => 'yaml')->parse;
die "No configuration file found\n" unless $config;
die "No addresses to track listed in config\n" unless $config->{track};
$config->{inbox} //= [];
$config->{sent} //= [];
$config->{inbox} = [$config->{inbox}] unless ref $config->{inbox};
$config->{sent} = [$config->{sent}] unless ref $config->{sent};
$config->{inbox} = \@DEFAULT_INBOX unless @{$config->{inbox}};
my %track = %{$config->{track}};
my %addr_to_id = map {
my $id = $_;
my $track = $track{$id};
$track = [$track] unless ref $track;
map { $_ => $id } @$track
} keys %track;
my (%lastmsg, %lastaddr);
my $process_message = sub {
my ($msg, @people) = @_;
for my $addr (@people) {
($addr) = $addr =~ /<\s*(.+)\s*>/ if $addr =~ /</;
$addr =~ s/^\s+//;
$addr =~ s/\s+$//;
my $id = $addr_to_id{$addr};
next unless $id;
my $date = str2time $msg->header_raw('Date');
if (!exists $lastmsg{$id} || $lastmsg{$id} < $date) {
$lastmsg{$id} = $date;
$lastaddr{$id} = $addr;
}
}
};
for my $folder (@{$config->{inbox}}) {
next unless -e $folder;
say STDERR "Scanning $folder (inbox)" if $ENV{LASTMSG_DEBUG};
my $folder = Email::Folder->new($folder);
while (my $msg = $folder->next_message) {
my ($from) = grep { /^from$/i } $msg->header_names;
$from = $msg->header_raw($from);
if ($ENV{LASTMSG_DEBUG}) {
my ($mid) = grep { /^message-id$/i } $msg->header_names;
say STDERR 'Processing ', $msg->header_raw($mid), " from $from";
}
$process_message->($msg, $from);
}
}
for my $folder (@{$config->{sent}}) {
next unless -e $folder;
say STDERR "Scanning $folder (sent)" if $ENV{LASTMSG_DEBUG};
my $folder = Email::Folder->new($folder);
while (my $msg = $folder->next_message) {
my @hdrs = grep { /^(?:to|cc|bcc)$/i } $msg->header_names;
my @people;
for my $hdr (@hdrs) {
@people = (@people, split /,/, $msg->header_raw($hdr));
}
if ($ENV{LASTMSG_DEBUG}) {
my ($mid) = grep { /^message-id$/i } $msg->header_names;
say STDERR 'Processing ', $msg->header_raw($mid),
' sent to ', join ',', @people;
}
$process_message->($msg, @people);
}
}
my $idlen = max map { length } keys %track;
my $addrlen = max map { length } values %lastaddr;
for (sort { $lastmsg{$b} <=> $lastmsg{$a} } keys %lastmsg) {
my $time = format_time $lastmsg{$_};
printf $OUTPUT_FILEHANDLE "%-${idlen}s %-${addrlen}s %s\n", $_, $lastaddr{$_}, $time;
}
for (grep { !exists $lastmsg{$_} } sort keys %track) {
printf $OUTPUT_FILEHANDLE "%-${idlen}s %-${addrlen}s NOT FOUND\n", $_, ''
}
}
1;
__END__
=encoding utf-8
=head1 NAME
App::Lastmsg - last(1) semblance for your inbox
=head1 SYNOPSIS
use App::Lastmsg;
App::Lastmsg::run
=head1 DESCRIPTION
This module contains the implementation of the L<lastmsg(1)> script.
See that script's documentation for information on what it does.
=head1 SEE ALSO
L<lastmsg>
=head1 AUTHOR
Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2016-2017 by Marius Gavrilescu
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.24.1 or,
at your option, any later version of Perl 5 you may have available.
=cut
( run in 0.777 second using v1.01-cache-2.11-cpan-39bf76dae61 )