You keep forgetting about birthdays of people important to you? A simple Perl script running on your Linux server solves the problem sending birthday reminder mails to your email account.
Prerequisite: your server has a mailer daemon running with sendmail interface.
user@server> sudo bash
root@server# which sendmail
/usr/sbin/sendmail
If your server doesn’t yet have a mailer daemon, you can install one with this command:
root@server# apt-get install sendmail
There is no need to open SMTP port 25 for incoming connections, but unless the script sends reminders to an account on the same server, your server needs to be able to connect to remote hosts on port 25. This can be verified with port scanners like nmap:
root@server# nmap -sT -p 25 remotehost.com
Birthday Reminder Script
Simply create a text or Excel CSV file with names and birthdays in the following line format under your home directory, where ‘user’ is your Unix user name:
user@server:/home/user/birthdays# cat bday.csv
Justin Acquaintance,9-jan
Mark Mustermann,10-jan-1934
Sonia Goodfriend,16-jan-1998
So the birthday data has names separated from dates by a comma and accepts either full birth dates in ‘dd-mon-yyyy’ format or, for acquaintances, where we may only day and month of birth, a shortened ‘dd-mon’.
Under your home directory, create a folder ‘birthdays’ with the birthday data ‘bday.csv’ and a perl script ‘reminder.pl’ with the following content:
#!/usr/bin/perl
use strict;
use FindBin;
use Date::Parse;
my $ldir = "$FindBin::Bin";
my $mailx = '/usr/sbin/sendmail';
my $date = '/bin/date';
my $hostname = '/bin/hostname';
-x $mailx or die "Cannot execute $mailx";
-x $date or die "Cannot execute $date";
#Setting the initial variables
my $IFILE="$ldir/bday.csv";
my $mailerhost = `$hostname`;
$mailerhost =~ s/\s*$//;
my $MAILFROM="birthdays\@$mailerhost";
my $MAILID='[email protected]';
my $ADAY=60*60*24;
my $AWEEK=$ADAY*7;
# Retrieving today's date and month
my $dat = dateStr();
# Get time truncated to date 0:00:00
my $dt = str2time($dat);
# Reading the bday.csv file and finding the users whose
# date matches with today and writing to OFILE
my $bdfil;
my $reminded = 0;
open($bdfil, "<", $IFILE) or die "Cannot open $IFILE for read";
while (<$bdfil>) {
if (/^\s*([^\#\s][^,]*),\s*([\w\-\/]+)/) {
my ($name, $bday) = ($1,$2);
$name = trim($name);
my ($day,$mon,$year) = split(/[\-\/]/, $bday);
if (!defined($day) || !defined($mon)) {
print "Undefined day/month in $_\n";
next;
}
$day = trim($day);
$mon = uc(trim($mon));
$year = trim($year) if defined($year);
# Get time of birthday, years defaulting to present!
my $bddt = str2time("$day-$mon");
my $bddtdiff = $bddt - $dt;
if ($bddtdiff < 0) {
# Birthday is next year!
$bddt = str2time("$day-$mon-@{[yearOfTime($dt)+1]}");
$bddtdiff = $bddt - $dt;
}
#print "$name $bday $bddtdiff\n";
my $headline = "Birthday Reminder for $name: ";
my $body = "This is a reminder for an upcoming birthday: $name born at @{[uc($bday)]}\n";
my $subject;
my $age = '';
if ($year) {$age = (yearOfTime($bddt) - $year) . " years"; }
if ($bddtdiff == 0) {
$subject = $headline . "$age TODAY";
} elsif ($bddtdiff == $ADAY) {
$subject = $headline . "$age TOMORROW";
} elsif ($bddtdiff == $AWEEK) {
$subject = $headline . "$age next week";
}
if ($subject) {
$reminded = 1;
my $rdate =trim(`$date`);
print "Remind: $rdate $subject\n$body\n";
my $pip;
open $pip, "|-", $mailx, "-t"
or die "Cannot pipe to $mailx";
print $pip "To: $MAILID\n";
print $pip "From: $MAILFROM\n";
print $pip "Subject: $subject\n";
print $pip "Content-type: text/plain\n\n";
print $pip $body;
close $pip;
}
}
}
if (!$reminded) {
print trim(`$date`) . ": no reminders\n";
}
close $bdfil;
exit 0;
sub trim($) {
my $string = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}
sub yearOfTime($) {
my ($sec,$min,$hour,$mday,$mon,$year) = gmtime(shift);
return $year + 1900;
}
sub dateStr() {
my ($sec,$min,$hour,$mday,$mon,$year) = gmtime();
my @month=qw(jan feb mar apr may jun jul aug sep oct nov dec);
return "$mday-@{[ucfirst($month[$mon])]}-@{[$year+1900]}";
}
The script ‘reminder.pl’ will generate a reminder email a week ahead for people with full ‘dd-mon-yyyy’ birthdays. For everyone and short ‘dd-mon’ birthdays there will be reminders a day before and on the birthday.
Finally, add a script ‘birthday’ under /etc/cron.daily:
root@server:/etc/cron.daily# cat birthday
#!/bin/sh
/bin/su user /bin/sh -c "/home/user/birthdays/reminder.pl >> /home/user/birthdays/reminder.log 2>&1"
The server will now run the birthday reminder script once per day. Note we change users from root to regular user for security reasons.
Don’t forget to set executable flags on scripts:
user@server:/home/user/birthdays> chmod a+x reminder.pl
root@server:/etc/cron.daily# chmod a+x birthday
You’ll need to change the Unix user name from “user” to your account name under cron.daily and replace “[email protected]” with your email address in reminder.pl. That’s it. You should now be getting birthday reminders. For testing, manually execute the ‘birthday’ script under cron.daily. You’ll also want to add test birthday lines in ‘bday.csv’ for whenever you’ll be testing the scripts.
The complete sources are available here: birthdays.tgz
References
Sending email with Perl using the sendmail utility: https://www.tutorialspoint.com/perl/perl_sending_email.htm