Updated setbb
[clearscm.git] / bin / announceEmail.pl
index a0113a1..a864634 100755 (executable)
@@ -31,20 +31,33 @@ $Date: 2019/04/04 13:40:10 $
 
 =head1 SYNOPSIS
 
- Usage: announceEmail.pl [-usa|ge] [-h|elp] [-v|erbose] [-de|bug] [-da|emon]
+ Usage: announceEmail.pl [-usa|ge] [-h|elp] [-v|erbose] [-de|bug]
                          [-use|rname <username>] [-p|assword <password>]
-                         [-i|map <server]
+                         [-i|map <server>] [-t|imeout <secs>]
+                         [-an|nouce] [-ap|pend] [-da|emon] [-n|name <name>]
+                         [-uses|sl] [-useb|locking]
 
  Where:
-   -usa|ge:    Print this usage
-   -h|elp:     Detailed help
-   -v|erbose:  Verbose mode (Default: -verbose)
-   -de|bug:    Turn on debugging (Default: Off)
-   -da|emon:   Run in daemon mode (Default: -daemon)
-   -user|name: User name to log in with (Default: $USER)
-   -p|assword: Password to use (Default: prompted)
-   -i|map:     IMAP server to talk to (Default: defaria.com)
-   -uses|sl:   Whether or not to use SSL to connect (Default: False)
+   -usa|ge       Print this usage
+   -h|elp        Detailed help
+   -v|erbose     Verbose mode (Default: -verbose)
+   -de|bug       Turn on debugging (Default: Off)
+
+   -user|name    User name to log in with (Default: $USER)
+   -p|assword    Password to use (Default: prompted)
+   -i|map        IMAP server to talk to (Default: defaria.com)
+   -t|imeout <s> Timeout IMAP idle call (Sefault: 1200 seconds or 20 minutes)
+
+   -an|nounce    Announce startup (Default: False)
+   -ap|pend      Append to logfile (Default: Noappend)
+   -da|emon      Run in daemon mode (Default: -daemon)
+   -n|ame        Name of account (Default: imap)
+   -uses|sl      Whether or not to use SSL to connect (Default: False)
+   -useb|locking Whether to block on socket (Default: False)
+
+ Signals:
+   $SIG{USR1}:   Toggles debug option
+   $SIG{USR2}:   Reestablishes connection to IMAP server
 
 =head1 DESCRIPTION
 
@@ -70,8 +83,12 @@ use lib "$FindBin::Bin/../lib";
 
 use Display;
 use Logger;
+use Speak;
+use TimeUtils;
 use Utils;
 
+local $0 = "$FindBin::Script " . join ' ', @ARGV;
+
 my $defaultIMAPServer = 'defaria.com';
 my $IMAP;
 my %unseen;
@@ -88,33 +105,62 @@ my @greetings = (
   'I was looking in your inbox and found a message',
   'Not sure you want to hear this message',
   'Good news',
+  "What's this? A new message",
 );
 
+my $icon          = '/home/andrew/.icons/Thunderbird.jpg';
+my $notifyTimeout = 5 * 1000;
+my $IMAPTimeout   = 20 * 60;
+
 my %opts = (
-  usage    => sub { pod2usage },
-  help     => sub { pod2usage(-verbose => 2)},
-  verbose  => sub { set_verbose },
-  debug    => sub { set_debug },
-  daemon   => 1,
-  username => $ENV{USER},
-  password => $ENV{PASSWORD},
-  imap     => $defaultIMAPServer,
-  usessl   => 0,
+  usage       => sub { pod2usage },
+  help        => sub { pod2usage(-verbose => 2)},
+  verbose     => sub { set_verbose },
+  debug       => sub { set_debug },
+  daemon      => 1,
+  timeout     => $IMAPTimeout,
+  username    => $ENV{USER},
+  password    => $ENV{PASSWORD},
+  imap        => $defaultIMAPServer,
 );
 
+sub notify($) {
+  my ($msg) = @_;
+
+  my $cmd = "notify-send -i $icon -t $notifyTimeout '$msg'";
+
+  Execute $cmd;
+
+  return;
+} # notify
+
 sub interrupted {
   if (get_debug) {
-    $log->msg("Turning off debugging");
+    notify 'Turning off debugging';
     set_debug 0;
   } else {
-    $log->msg("Turning on debugging");
+    notify ('Turning on debugging');
     set_debug 1;
   } # if
 
   return;
 } # interrupted
 
+sub Connect2IMAP;
+sub MonitorMail;
+
+sub restart {
+  my $msg = "Re-establishing connection to $opts{imap} as $opts{username}";
+
+  $log->dbug($msg);
+
+  Connect2IMAP;
+
+  MonitorMail;
+} # restart
+
 $SIG{USR1} = \&interrupted;
+$SIG{USR2} = \&restart;
 
 sub unseenMsgs() {
   $IMAP->select('inbox') or
@@ -124,16 +170,20 @@ sub unseenMsgs() {
 } # unseenMsgs 
 
 sub Connect2IMAP() {
-  $log->msg("Connecting to $opts{imap} as $opts{username}");
+  $log->dbug("Connecting to $opts{imap} as $opts{username}");
+
+  # Destroy any old connections
+  undef $IMAP;
 
   $IMAP = Mail::IMAPTalk->new(
-    Server   => $opts{imap},
-    Username => $opts{username},
-    Password => $opts{password},
-    UseSSL   => $opts{usessl},
+    Server      => $opts{imap},
+    Username    => $opts{username},
+    Password    => $opts{password},
+    UseSSL      => $opts{usessl},
+    UseBlocking => $opts{useblocking},
   ) or $log->err("Unable to connect to IMAP server $opts{imap}: $@", 1);
 
-  $log->msg('Connected');
+  $log->dbug("Connected to $opts{imap} as $opts{username}");
 
   # Focus on INBOX only
   $IMAP->select('inbox');
@@ -146,17 +196,22 @@ sub Connect2IMAP() {
 } # Connect2IMAP
 
 sub MonitorMail() {
+  MONITORMAIL:
+  $log->dbug("Top of MonitorMail loop");
+
   # First close and reselect the INBOX to get its current status
   $IMAP->close;
   $IMAP->select('INBOX')
     or $log->err("Unable to select INBOX - ". $IMAP->errstr(), 1);
 
+  $log->dbug("Closed and reselected INBOX");
   # Go through all of the unseen messages and add them to %unseen if they were
   # not there already from a prior run and read
   my %newUnseen = unseenMsgs;
 
   # Now clean out any messages in %unseen that were not in the %newUnseen and
   # marked as previously read
+  $log->dbug("Cleaning out unseen");
   for (keys %unseen) {
     if (defined $newUnseen{$_}) {
       if ($unseen{$_}) {
@@ -167,6 +222,7 @@ sub MonitorMail() {
     } # if
   } # for
 
+  $log->dbug("Processing new unseen messages");
   for (keys %newUnseen) {
     next if $unseen{$_};
 
@@ -187,43 +243,71 @@ sub MonitorMail() {
     # Google Talk doesn't like #
     $subject =~ s/\#//g;
 
+    # Remove long strings of numbers like order numbers. They are uninteresting
+    my $longNumber = 5;
+    $subject =~ s/\s+\S*\d{$longNumber,}\S*\s*//g;
+
     # Now speak it!
     my $logmsg = "From $from $subject";
 
     my $greeting = $greetings[int rand $#greetings];
-    my $msg      = "$greeting from $from... " . quotemeta $subject;
-       $msg      =~ s/\"/\\"/g;
-
-    # Log it
-    $log->msg($logmsg);
-
-    my $hour = (localtime)[2];
+    my $msg      = "$greeting from $from... $subject";
+    my $hour     = (localtime)[2];
 
     # Only announce if after 6 Am. Note this will announce up until
     # midnight but that's ok. I want midnight to 6 Am as silent time.
-    if ($hour > 6) {
-      my $cmd = "/usr/local/bin/gt \"$msg\"";
-      my ($status, @output) = Execute $cmd;
-
-      if ($status) {
-        $log->err("Unable to execute $cmd" . join("\n", @output));
-      } # if
+    $log->dbug("About to speak/log");
+    if ($hour >= 7) {
+      $log->msg($logmsg);
+      $log->dbug("Calling speak");
+      speak $msg, $log;
+    } else {
+      $log->msg("$logmsg [silent nighttime]");
     } # if
 
     $unseen{$_} = 1;
   } # for
 
+  # Let's time things
+  my $startTime = time;
+
   # Re-establish callback
-  $IMAP->idle(\&MonitorMail);
+  $log->dbug("Calling IMAP->idle");
+  eval {
+    $IMAP->idle(\&MonitorMail, $opts{timeout})
+  };
+
+  my $msg = 'Returned from IMAP->idle ';
+
+  if ($@) {
+    speak($msg . $@, $log);
+  } else {
+    $log->msg($msg . 'no error');
+  } # if
+
+  # If we return from idle then the server went away for some reason. With Gmail
+  # the server seems to time out around 30-40 minutes. Here we simply reconnect
+  # to the imap server and continue to MonitorMail.
+  unless ($IMAP->get_response_code('timeout')) {
+    $msg = "IMAP Idle for $opts{name} timed out in " . howlong $startTime, time;
+
+    speak $msg;
 
-  # Should not get here
-  $log->err("Unable to re-establish IDLE callback from IMAP server $opts{imap}", 1);
+    $log->msg($msg);
+  } # unless
+
+  restart;
 } # MonitorMail
 
 END {
-  $IMAP->quit if $IMAP;
+  # If $log is not yet defined then the exit is not unexpected
+  if ($log) {
+    my $msg = "$FindBin::Script $opts{name} ending unexpectedly!";
+
+    speak $msg, $log;
 
-  $log->msg("$FindBin::Script ending unexpectedly!");
+    $log->err($msg);
+  } # if
 } # END
 
 ## Main
@@ -235,40 +319,56 @@ GetOptions(
   'debug',
   'daemon!',
   'username=s',
+  'name=s',
   'password=s',
   'imap=s',
+  'timeout=i',
   'usessl',
-  'sleep',
-);
+  'useblocking',
+  'announce!',
+  'append',
+) || pod2usage;
 
 unless ($opts{password}) {
   verbose "I need $opts{username}'s password";
   $opts{password} = GetPassword;
 } # unless
 
-$opts{debug} = get_debug;
+$opts{name} //= $opts{imap};
+
+if ($opts{username} =~ /.*\@(.*)$/) {
+  $opts{name} = $1;
+} # if
 
 if ($opts{daemon}) {
-  EnterDaemonMode unless defined $DB::OUT;
+  # Perl complains if we reference $DB::OUT only once
+  no warnings;
+  EnterDaemonMode unless defined $DB::OUT or get_debug;
+  use warnings;
 } # if
 
 $log = Logger->new(
-  path        => '/var/log',
+  path        => '/var/local/log',
+  name        => "$Logger::me.$opts{name}",
   timestamped => 'yes',
-  append      => 'yes',
+  append      => $opts{append},
 );
 
 Connect2IMAP;
 
-my $msg = "Now monitoring email for $opts{username}\@$opts{imap}";
+if ($opts{username} =~ /(.*)\@/) {
+  $opts{user} = $1;
+} else {
+  $opts{user} = $opts{username};
+} # if
 
-$log->msg($msg);
+my $msg = "Now monitoring email for $opts{user}\@$opts{name}";
 
-my $cmd = "/usr/local/bin/gt \"$msg\"";
+speak $msg, $log if $opts{announce};
 
-my ($status, @output) = Execute $cmd;
+$log->msg($msg);
 
-$IMAP->idle(\&MonitorMail);
+MonitorMail;
 
 # Should not get here
 $log->err("Falling off the edge of $0", 1);