First, most of the email providers give you a way to form-fill an email template. So, you can just log into your admin portal, type out the form mail, then look at their API to find out how to deliver the list of addresses and names. That's the easiest way.
If you really want to use SMTP, maybe for reasons you haven't told us, then like corion mentioned, you first should think of how to resume the mailing after it gets interrupted, because it would be bad (and expensive) to have to re-start from the beginning of the list and send some of them twice. You can either sort the emails and log as you send them so that you know where to pick up from, or you could write your progress to a database table in a way that you can later select only the email addresses that haven't been sent the email yet. Another method is to make a "spool" table where you queue one record for each email that you want to send, then delete one row from that table after each email is sent. This lets you separate the seteps of preparing the data to email, and delivering the emails, so you can inspect the results of the first phase before you have sent anything, and if something is wrong you just delete all the records and try again. When the queue looks accurate, then you begin sending, and deleting the record or updating it with the results of the send attempt.
In your code, this looks suspicious:
my @to;
for my $em ( @$records ) {
push @to, @$em;
}
It looks like each element of $records is an array of *fields*, right? with one field being the email and one field being the name? I would think you would just do
# Slice makes it return an arrayref of hashrefs
my $records = $query->fetchall_arrayref({});
my $transport= ...
for my $record (@$records) {
mail($from, $record->{to}, $subject, $record->{name});
}
sub mail {
my ($from, $to, $subject, $name)= @_;
my $html= .... # form-fill the name into an html template
my $email= ...
sendmail($email, { transport => $transport });
}
You should definitely use a persistent mail transport. Here's a snippet from one of my systems:
$transport= Email::Sender::Transport::SMTP::Persistent->new(
host => $host,
ssl => ($port == 465)? 1 : ($port == 587)? 'starttls' : 0,
port => $port,
$user? (sasl_username => $user) : (),
$pass? (sasl_password => $pass) : (),
debug => $log->is_trace,
);
That makes one SSL connection to the server and then sends all the mail through it without needing to re-initialize the SSL each time. It's massively faster than re-connecting for each email.
If you're looking for a good way to assemble a quality email message, I recommend Email::MIME::CreateHTML. Here are some other snippets you might find helpful:
# Assuming you start with an HTML document for your email:
$text= HTML::FormatText::WithLinks->new(unique_links => 1)
->parse($html);
(it's good practice to include the text-only version of your html email, and probably helps not getting flagged as spam)
# Because most mail clients don't properly process <style> blocks,
# we have to embed the style onto each individual element.
# This class does the job for us.
my $inliner= CSS::Inliner->new();
$inliner->read({ html => $html, charset => 'utf8' });
$html= $inliner->inlinify;
I forget why my code is calling CSS::Inliner when it also sets inline_css => 1 in the options to create_html below. I'll let you discover if that's necessary :-)
# Build the email. The resolver will find all external images
# and stylesheets and suck them in as multiple parts.
my $email= Email::MIME->create_html(
header => [ ], # we use the unicode-enabled setters, instead
text_body_attributes =>
{ charset => 'UTF-8', encoding => 'quoted-printable' },
text_body => encode('UTF-8', $text), # text gets assigned as-is
body_attributes =>
{ charset => 'UTF-8', encoding => 'quoted-printable' },
body => $html, # body gets assigned as body_str
embed => 1,
base => "https://YOUR_WEBSITE",
# Prevent excessive lookups of resources
object_cache => $self->resource_cache,
inline_css => 1,
%$args,
);
# These methods properly handle unicode, which is why we use them
# instead of passing these to the constructor
$email->header_str_set(To =>
(ref $to eq 'ARRAY'? join(', ',@$to) : ''.$to));
$email->header_str_set(From =>
(ref $from eq 'ARRAY'? join(', ',@$from) : ''.$from));
$email->header_str_set(Subject => ''.$subject);
Note all the details about utf-8. Sadly Email::MIME::CreateHTML doesn't have nice support for that yet. Email::MIME doesn't really either - I mean the options and methods exist, they just aren't the defaults.
|