1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
use Email::Address;
use List::MoreUtils 'any';
use Path::Class;
use Text::CSV;
sub existing_addresses {
my ($addressbook) = @_;
open my $fh, '-|', (
'abook',
'--convert',
'--infile', $addressbook,
'--outformat', 'csv'
) or die "Couldn't run abook: $!";
my $csv = Text::CSV->new;
my @out;
while (my $row = $csv->getline($fh)) {
push @out, Email::Address->new($row->[0], $row->[1]);
}
$fh->close;
return @out;
}
my @exclude_patterns = (
qr/^"?=\?/, # quoted-printable encoded addresses - punt for now
qr/<notifications\@github\.com>/,
qr/\@(?:.*\.)?stripe\.io\b/,
qr/(?:no|do-?not)-?reply/i,
qr/via (.*)"? <\1\@/,
qr/support\@stripe\.com/,
);
sub maildir_senders {
my ($maildir) = @_;
my $all_mail = dir($maildir)->subdir('[Gmail].All Mail')->subdir('cur');
my %senders;
for my $file ($all_mail->children) {
my $fh = $file->openr;
while (<$fh>) {
last if /^$/;
next unless /^(?:From|Sender): /;
my @addresses = Email::Address->parse($_);
$senders{$_->format} = $_ for @addresses;
}
$fh->close;
}
return grep {
my $address = $_->format;
!any { $address =~ $_ } @exclude_patterns
} values %senders;
}
{
my $addressbook = "$ENV{HOME}/.abook/addressbook";
my $maildir = "$ENV{HOME}/Maildir";
my @addresses = (
existing_addresses($addressbook),
maildir_senders($maildir),
# ... (hardcoded things?)
);
my %addresses = map { $_->address => $_ } @addresses;
@addresses = sort values %addresses;
file($addressbook)->move_to("$addressbook.bak");
open my $fh, '|-', (
'abook',
'--convert',
'--informat', 'csv',
'--outformat', 'abook',
'--outfile', $addressbook,
) or die "Couldn't run abook: $!";
for my $address (@addresses) {
$fh->print(qq{"@{[$address->phrase || $address->address]}","@{[$address->address]}","","",""\n});
}
}
|