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