Removed old tunnel. Implemented speak bin
[clearscm.git] / bin / announceEmail.pl
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: announceEmail.pl,v $
6
7 Monitors an IMAP Server and announce incoming emails by extracting the subject
8 line and from line and then pushing that into "GoogleTalk".
9
10 =head1 VERSION
11
12 =over
13
14 =item Author
15
16 Andrew DeFaria <Andrew@DeFaria.com>
17
18 =item Revision
19
20 $Revision: 1.2 $
21
22 =item Created:
23
24 Thu Apr  4 13:40:10 MST 2019
25
26 =item Modified:
27
28 $Date: 2019/04/04 13:40:10 $
29
30 =back
31
32 =head1 SYNOPSIS
33
34  Usage: announceEmail.pl [-usa|ge] [-h|elp] [-v|erbose] [-de|bug] [-da|emon]
35                          [-use|rname <username>] [-p|assword <password>]
36                          [-i|map <server]
37
38  Where:
39    -usa|ge:      Print this usage
40    -h|elp:       Detailed help
41    -v|erbose:    Verbose mode (Default: -verbose)
42    -de|bug:      Turn on debugging (Default: Off)
43    -da|emon:     Run in daemon mode (Default: -daemon)
44    -user|name:   User name to log in with (Default: $USER)
45    -p|assword:   Password to use (Default: prompted)
46    -n|ame:       Name of account (Default: imap)
47    -i|map:       IMAP server to talk to (Default: defaria.com)
48    -uses|sl:     Whether or not to use SSL to connect (Default: False)
49    -useb|locking Whether to block on socket (Default: False)
50    -a-nnounce    Announce startup (Default: False)
51
52 =head1 DESCRIPTION
53
54 This script will connect to an IMAP server, login and then monitor the user's
55 INBOX. When new messages arrive it will extract the From address and Subject
56 from the message and compose a message to be used by "Google Talk" to announce
57 the email. The message will be similar to:
58
59   "<From> emailed <Subject>"
60
61 =cut
62
63 use strict;
64 use warnings;
65
66 use FindBin;
67 use Getopt::Long;
68 use Pod::Usage;
69 use Mail::IMAPTalk;
70 use MIME::Base64;
71
72 use lib "$FindBin::Bin/../lib";
73
74 use Display;
75 use Logger;
76 use Speak;
77 use TimeUtils;
78 use Utils;
79
80 my $defaultIMAPServer = 'defaria.com';
81 my $IMAP;
82 my %unseen;
83 my $log;
84
85 my @greetings = (
86   'Incoming message',
87   'You have received a new message',
88   'Hey I found this in your inbox',
89   'For some unknown reason this guy send you a message',
90   'Did you know you just got a message',
91   'Potential spam',
92   'You received a communique',
93   'I was looking in your inbox and found a message',
94   'Not sure you want to hear this message',
95   'Good news',
96 );
97
98 my %opts = (
99   usage       => sub { pod2usage },
100   help        => sub { pod2usage(-verbose => 2)},
101   verbose     => sub { set_verbose },
102   debug       => sub { set_debug },
103   daemon      => 1,
104   username    => $ENV{USER},
105   password    => $ENV{PASSWORD},
106   imap        => $defaultIMAPServer,
107   usessl      => 0,
108   useblocking => 0,
109   announce    => 0,
110 );
111
112 sub interrupted {
113   if (get_debug) {
114     $log->msg("Turning off debugging");
115     set_debug 0;
116   } else {
117     $log->msg("Turning on debugging");
118     set_debug 1;
119   } # if
120
121   return;
122 } # interrupted
123
124 $SIG{USR1} = \&interrupted;
125
126 sub unseenMsgs() {
127   $IMAP->select('inbox') or
128     $log->err("Unable to select inbox: " . get_last_error(), 1);
129
130   return map { $_=> 0 } @{$IMAP->search('not', 'seen')};
131 } # unseenMsgs 
132
133 sub Connect2IMAP() {
134   $log->dbug("Connecting to $opts{imap} as $opts{username}");
135
136   $IMAP = Mail::IMAPTalk->new(
137     Server      => $opts{imap},
138     Username    => $opts{username},
139     Password    => $opts{password},
140     UseSSL      => $opts{usessl},
141     UseBlocking => $opts{useblocking},
142   ) or $log->err("Unable to connect to IMAP server $opts{imap}: $@", 1);
143
144   $log->dbug("Connected to $opts{imap} as $opts{username}");
145
146   # Focus on INBOX only
147   $IMAP->select('inbox');
148
149   # Setup %unseen to have each unseen message index set to 0 meaning not read
150   # aloud yet
151   %unseen = unseenMsgs;
152
153   return;
154 } # Connect2IMAP
155
156 sub MonitorMail() {
157   MONITORMAIL:
158
159   # First close and reselect the INBOX to get its current status
160   $IMAP->close;
161   $IMAP->select('INBOX')
162     or $log->err("Unable to select INBOX - ". $IMAP->errstr(), 1);
163
164   # Go through all of the unseen messages and add them to %unseen if they were
165   # not there already from a prior run and read
166   my %newUnseen = unseenMsgs;
167
168   # Now clean out any messages in %unseen that were not in the %newUnseen and
169   # marked as previously read
170   for (keys %unseen) {
171     if (defined $newUnseen{$_}) {
172       if ($unseen{$_}) {
173         delete $newUnseen{$_};
174       } # if
175     } else {
176       delete $unseen{$_}
177     } # if
178   } # for
179
180   for (keys %newUnseen) {
181     next if $unseen{$_};
182
183     my $envelope = $IMAP->fetch($_, '(envelope)');
184     my $from     = $envelope->{$_}{envelope}{From};
185     my $subject  = $envelope->{$_}{envelope}{Subject};
186        $subject //= 'Unknown subject';
187
188     # Extract the name only when the email is of the format "name <email>"
189     if ($from =~ /^"?(.*?)"?\s*\<(\S*)>/) {
190       $from = $1 if $1 ne '';
191     } # if
192
193     if ($subject =~ /=?\S+?(Q|B)\?(.+)\?=/) {
194       $subject = decode_base64($2);
195     } # if
196
197     # Google Talk doesn't like #
198     $subject =~ s/\#//g;
199
200     # Now speak it!
201     my $logmsg = "From $from $subject";
202
203     my $greeting = $greetings[int rand $#greetings];
204     my $msg      = "$greeting from $from... $subject";
205        $msg      =~ s/\"/\\"/g;
206
207     my $hour = (localtime)[2];
208
209     # Only announce if after 6 Am. Note this will announce up until
210     # midnight but that's ok. I want midnight to 6 Am as silent time.
211     if ($hour >= 7) {
212       speak $msg, $log;
213       $log->msg($logmsg);
214     } else {
215       $log->msg("$logmsg [silent]");
216     } # if
217
218     $unseen{$_} = 1;
219   } # for
220
221   # Re-establish callback
222   eval { $IMAP->idle(\&MonitorMail) };
223
224   # If we return from idle then the server went away for some reason. With Gmail
225   # the server seems to time out around 30-40 minutes. Here we simply reconnect
226   # to the imap server and continue to MonitorMail.
227   $log->dbug("MonitorMail: Connection to $opts{imap} ended. Reconnecting");
228
229   # Destroy current IMAP connection
230   $log->dbug("MonitorMail: Destorying IMAP connection to $opts{imap}");
231
232   undef $IMAP;
233
234   # Re-establish connection
235   Connect2IMAP;
236
237   $log->dbug("MonitorMail: Reconnected to IMAP server $opts{imap}");
238
239   # MonitorMail again - the dreaded goto! Seems the cleanest way to restart
240   # in this instance. I could call MonitorMail() recursively but that would
241   # leave junk on the stack.
242   $log->dbug('MonitorMail: Going back to the top of the loop');
243
244   goto MONITORMAIL;
245
246   return; # To make perlcritic happy
247 } # MonitorMail
248
249 END {
250   # If $log is not yet defined then the exit is not unexpected
251   if ($log) {
252     my $msg = "$FindBin::Script ending unexpectedly!";
253
254     speak $msg, $log;
255
256     $log->err($msg);
257   } # if
258 } # END
259
260 ## Main
261 GetOptions(
262   \%opts,
263   'usage',
264   'help',
265   'verbose',
266   'debug',
267   'daemon!',
268   'username=s',
269   'name=s',
270   'password=s',
271   'imap=s',
272   'usessl',
273   'useblocking',
274   'announce!',
275 ) || pod2usage;
276
277 unless ($opts{password}) {
278   verbose "I need $opts{username}'s password";
279   $opts{password} = GetPassword;
280 } # unless
281
282 $opts{name} //= $opts{imap};
283
284 if ($opts{username} =~ /.*\@(.*)$/) {
285   $opts{name} = $1;
286 } # if
287
288 if ($opts{daemon}) {
289   # Perl complains if we reference $DB::OUT only once
290   my $foo = $DB::OUT;
291   EnterDaemonMode unless defined $DB::OUT;
292 } # if
293
294 $log = Logger->new(
295   path        => '/var/log',
296   name        => "$Logger::me.$opts{name}",
297   timestamped => 'yes',
298   append      => 'yes',
299 );
300
301 Connect2IMAP;
302
303 if ($opts{username} =~ /(.*)\@/) {
304   $opts{user} = $1;
305 } else {
306   $opts{user} = $opts{username};
307 } # if
308
309 my $msg = "Now monitoring email for $opts{user}\@$opts{name}";
310
311 speak $msg, $log if $opts{announce};
312
313 $log->msg($msg);
314
315 MonitorMail;
316
317 # Should not get here
318 $log->err("Falling off the edge of $0", 1);