#!/usr/bin/perl use warnings; use strict; use autodie; use Data::Dumper; use Email::Sender::Transport::SMTP::Persistent; use Email::Stuffer; use Encode; use Encode::Locale; use Encode::MIME::Header; use Getopt::Long; use List::Util; use MCE; use MCE::Loop; use MCE::Flow; use MCE::Shared; use Path::Tiny; use Pod::Usage; use Time::HiRes qw(gettimeofday tv_interval); # apt install cpanminus libemail-sender-perl libmce-perl libemail-stuffer-perl libpath-tiny-perl # cpanm MCE::Shared use open ':encoding(locale)'; @ARGV = map { decode(locale => $_, 1) } @ARGV; # #### # configurable settings # #### my $transport = Email::Sender::Transport::SMTP::Persistent->new({ host => 'localhost', port => 25, #debug => 1, }); my $thread_count = 64; my $chunksize = 1; # #### # end config # #### =head1 NAME mailer.pl - send lots of mails =head1 SYNOPSIS mailer.pl options ... Options: --help, -h short help message --to recipient(s) --from from --subject subject --content-html body of the mail (html part) (optional) --content-txt body of the mail (text part) --debug output extra debug information =cut my %opts = (); Getopt::Long::Configure ("bundling"); pod2usage(-verbose => 0) if (@ARGV== 0); GetOptions(\%opts, "help|h", "to=s", "from=s", "subject=s", "content-html=s", "content-txt=s", "debug") or pod2usage(2); pod2usage(0) if $opts{help}; sub trim { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } my $subject = $opts{subject}; my $from = $opts{from}; my $content_html; my $content_txt; die "Error: subject not set\n" unless ($opts{subject}); die "Error: from not set\n" unless ($opts{from}); die "Error: content-txt not set\n" unless ($opts{"content-txt"}); die "Error: can't read content-txt: $!\n" unless (-r $opts{"content-txt"}); die "Error: can't read content-html: $!\n" if ($opts{"content-html"} and not -r $opts{"content-html"}); if ($opts{"content-html"}) { $content_html = path($opts{"content-html"})->slurp_utf8; } $content_txt = path($opts{"content-txt"})->slurp_utf8; print "Building email ... " if $opts{debug}; # create mail # To will be filled when sending my $msg = Email::Stuffer->new(); $msg->from($from); $msg->subject(trim($subject)); $msg->text_body($content_txt); $msg->html_body($content_html); $msg->header("Precedence", "bulk"); #$msg->header("List-Unsubscribe", ""); $msg->transport($transport); print "done\n" if $opts{debug}; print "creating workers\n" if $opts{debug}; print "Sending\n"; my $send_time = [gettimeofday]; my $queue = MCE::Shared->queue(await => 1, fast => 1); mce_flow { task_name => ['producer', 'sender'], max_workers => [1, $thread_count]}, sub { if ($opts{to} =~ m/.+\@.+\..+/) { $queue->enqueue($opts{to}); } else { if (-r $opts{to}) { open my $fh, "<", $opts{to}; while (<$fh>) { $queue->enqueue($_); $queue->await($thread_count); } close $fh; } else { print STDERR "Error: can't read recipient file: $!\n"; } } $queue->end(); }, sub { while (defined (my $to = $queue->dequeue())) { $msg->to($to); $msg->send(); print "sent mail in worker ".MCE->wid."\n" if $opts{debug}; } $transport->disconnect; }; print "done after ".tv_interval($send_time)." seconds\n";