7e840e10ce7a20032dc573d5d14abcd8346c2e9a
[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.1 $
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    -use|rname: User name to log in with (Default: $USER)
45    -p|assword: Password to use (Default: prompted)
46    -i|map:     IMAP server to talk to (Default: defaria.com)
47
48 =head1 DESCRIPTION
49
50 This script will connect to an IMAP server, login and then monitor the user's
51 INBOX. When new messages arrive it will extract the From address and Subject
52 from the message and compose a message to be used by "Google Talk" to announce
53 the email. The message will be similar to:
54
55   "<From> emailed <Subject>"
56
57 =cut
58
59 use strict;
60 use warnings;
61
62 use FindBin;
63 use Getopt::Long;
64 use Pod::Usage;
65 use Mail::IMAPTalk;
66 use MIME::Base64;
67
68 use lib "$FindBin::Bin/../lib";
69
70 use Display;
71 use Logger;
72 use Utils;
73
74 my $defaultIMAPServer = 'defaria.com';
75 my $IMAP;
76 my %unseen;
77 my $log;
78
79 my %opts = (
80   usage    => sub { pod2usage },
81   help     => sub { pod2usage(-verbose => 2)},
82   verbose  => sub { set_verbose },
83   debug    => sub { set_debug },
84   daemon   => 1,
85   username => $ENV{USER},
86   password => $ENV{PASSWORD},
87   imap     => $defaultIMAPServer,
88 );
89
90 sub interrupted {
91   if (get_debug) {
92     $log->msg("Turning off debugging");
93     set_debug 0;
94   } else {
95     $log->msg("Turning on debugging");
96     set_debug 1;
97   } # if
98
99   return;
100 } # interrupted
101
102 $SIG{USR1} = \&interrupted;
103
104 sub debugit($) {
105   my ($msg) = @_;
106
107   $log->msg($msg) if get_debug;
108
109   return;
110 } # logit
111
112 sub unseenMsgs() {
113   $IMAP->select('inbox') or
114     $log->err("Unable to select inbox: " . get_last_error(), 1);
115
116   return map { $_=> 0 } @{$IMAP->search('not', 'seen')};
117 } # unseenMsgs 
118
119 sub Connect2IMAP() {
120   $log->msg("Connecting to $opts{imap} as $opts{username}...", 1);
121
122   $IMAP = Mail::IMAPTalk->new(
123     Server   => $opts{imap},
124     Username => $opts{username},
125     Password => $opts{password},
126   ) or $log->err("Unable to connect to IMAP server $opts{imap}: $@", 1);
127
128   $log->msg(' connected');
129
130   # Focus on INBOX only
131   $IMAP->select('inbox');
132
133   # Setup %unseen to have each unseen message index set to 0 meaning not read
134   # aloud yet
135   %unseen = unseenMsgs;
136
137   return;
138 } # Connect2IMAP
139
140 sub MonitorMail() {
141   while () {
142     # First close and reselect the INBOX to get its current status
143     debugit "Reconnecting to INBOX";
144     $IMAP->close;
145     $IMAP->select('INBOX')
146       or $log->err("Unable to select INBOX - ". $IMAP->errstr(), 1);
147
148     # Go through all of the unseen messages and add them to %unseen if they were
149     # not there already from a prior run and read
150     my %newUnseen = unseenMsgs;
151
152     # Now clean out any messages in %unseen that were not in the %newUnseen and
153     # marked as previously read
154     for (keys %unseen) {
155       if (defined $newUnseen{$_}) {
156         if ($unseen{$_}) {
157           delete $newUnseen{$_};
158         } # if
159       } else {
160         delete $unseen{$_}
161       } # if
162     } # for
163
164     debugit "Processing newUnseen";
165     for (keys %newUnseen) {
166       next if $unseen{$_};
167
168       my $envelope = $IMAP->fetch($_, '(envelope)');
169       my $from     = $envelope->{$_}{envelope}{From};
170       my $subject  = $envelope->{$_}{envelope}{Subject};
171          $subject //= 'Unknown subject';
172
173       # Extract the name only when the email is of the format "name <email>"
174       if ($from =~ /^"?(.*?)"?\s*\<(\S*)>/) {
175         $from = $1 if $1 ne '';
176       } # if
177
178       if ($subject =~ /=?\S+?(Q|B)\?(.+)\?=/) {
179         $subject = decode_base64($2);
180       } # if
181
182       # Google Talk doesn't like #
183       $subject =~ s/\#//g;
184
185       # Now speak it!
186       debugit "Speaking message from $from";
187       my $logmsg = "From $from $subject";
188
189       my $msg = "Message from $from... " . quotemeta $subject;
190          $msg =~ s/\"/\\"/g;
191
192       # Log it
193       $log->msg($logmsg);
194
195       debugit $logmsg;
196
197       my $cmd = "/usr/local/bin/gt \"$msg\"";
198
199       my $hour = (localtime)[2];
200
201       # Only announce if after 6 Am. Note this will announce up until
202       # midnight but that's ok. I want midnight to 6 Am as silent time.
203       if ($hour > 6) {
204         my ($status, @output) = Execute $cmd;
205
206         if ($status) {
207           $log->err("Unable to execute $cmd" . join("\n", @output));
208         } # if
209       } # if
210
211       $unseen{$_} = 1;
212     } # for
213   } # while
214
215   return;
216 } # MonitorMail
217
218 END {
219   $IMAP->quit if $IMAP;
220
221   $log->msg("$FindBin::Script ending!");
222 } # END
223
224 ## Main
225 GetOptions(
226   \%opts,
227   'usage',
228   'help',
229   'verbose',
230   'debug',
231   'daemon!',
232   'username=s',
233   'password=s',
234   'imap=s',
235   'sleep',
236 );
237
238 unless ($opts{password}) {
239   verbose "I need $opts{username}'s password";
240   $opts{password} = GetPassword;
241 } # unless
242
243 $opts{debug} = get_debug;
244
245 EnterDaemonMode if $opts{daemon};
246
247 $log = Logger->new(
248   path        => '/var/log',
249   timestamped => 'yes',
250   append      => 'yes',
251 );
252
253 Connect2IMAP;
254
255 my $msg = "Now monitoring email for $opts{username}\@$opts{imap}";
256
257 $log->msg($msg);
258
259 my $cmd = "/usr/local/bin/gt \"$msg\"";
260
261 my ($status, @output) = Execute $cmd;
262
263 $IMAP->idle(\&MonitorMail);
264