New away script
[tpope-extra.git] / perl / away-tpope
1 #!/usr/bin/perl -w
2 # $Id$
3 # -*- perl -*- vim:set ft=perl sw=4 sts=4:
4
5 use strict;
6 use vars qw(%state @ssh $last $pipe);
7 @ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20");
8 $pipe = "/tmp/.away-tpope";
9
10 my $arg=shift || "";
11 daemonize() if ($arg eq "-d");
12 daemon() if ($arg eq "-D");
13
14
15 if($arg eq "activity" || $arg eq "away") {
16     if(exists($ARGV[0])) {
17         die "Daemon not running\n" unless (-p $pipe);
18         my $a = join(" ",@ARGV);
19         $a =~ s/^-c$//;
20         open FIFO, ">$pipe";
21         print FIFO "$arg $a";
22     } else {
23         $arg =~ s/^away$/customaway/;
24         load();
25         print $state{$arg}."\n" if($state{$arg});
26     }
27 } else {
28     exit 1;
29 }
30
31 sub daemon {
32     open TMP, ">/tmp/.away-tpope.pid";
33     print TMP "$$\n";
34     close TMP;
35     load();
36     $SIG{'INT'} = \&quit_handler;
37     $SIG{'TERM'} = \&quit_handler;
38     $SIG{'QUIT'} = \&quit_handler;
39     $SIG{'HUP'} = \&cycle;
40     die "Daemon already running\n" if (-r "/tmp/.tpope-away.pid");
41     unless (-p $pipe) {
42         unlink $pipe;
43         system("mkfifo", $pipe);
44     }
45     cycle();
46     $last=time;
47     while(1) {
48         main_loop();
49     }
50 }
51
52 sub daemonize {
53     chdir "/";
54     my $pid=fork();
55     if($pid) {
56         exit(0)
57     }
58     elsif(defined($pid)) {
59         daemon();
60     } else { exit(1); }
61 }
62
63 sub main_loop {
64     my $read="";
65     eval {
66         local $SIG{ALRM} = sub { die "alarm\n" };
67         alarm 1;
68         open FIFO, "<$pipe" and
69         $read = <FIFO>;
70         alarm 0;
71         close FIFO;
72     };
73     if($@) {
74         die unless $@ eq "alarm\n";
75         close FIFO;
76     }
77     if($read) {
78         chomp $read;
79         $read =~ s/^([a-z]*)( |$)//;
80         my $c = $1 || "";
81             if($c eq "activity") {
82                 custom_activity($read);
83             } elsif($c eq "away") {
84                 custom_away($read);
85             } else {
86                 print "Unknown command: $c\n";
87             }
88     }
89     if(time-$last >=300) {
90         local $SIG{'HUP'} = "IGNORE";
91         cycle();
92         $last=time;
93     }
94 }
95
96 sub cycle {
97     do_schedule();
98     do_hosts();
99     do_phone();
100     process_away();
101     save();
102     do_custom();
103 }
104
105 sub ping {
106     open(SOUT, ">&STDOUT");
107     close(STDOUT);
108     my $ret=!(system('ping','-q','-c','1',shift)>>8);
109     open(STDOUT, ">&SOUT");
110     close(SOUT);
111     return $ret;
112 }
113
114 sub do_schedule {
115     my @now=localtime(time);
116     my $now=$now[3]*60+$now[2];
117
118     open(SCHEDULE, "today --category=school|");
119     while(<SCHEDULE>) {
120         my ($begin, $end, $maxend);
121         next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
122         my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
123         $begin = $hs*60+$ms-25;
124         $maxend = $he*60+$me+5;
125         $end = ($begin+25)*3/4+($maxend-5)/4;
126         if($begin <= $now && $now < $end) {
127             $state{'class'} = $ev;
128             last;
129         } elsif ($state{'class'} eq $ev && $now > $maxend) {
130             $state{'class'} = '';
131         }
132     }
133     close(SCHEDULE);
134
135
136     open(SCHEDULE, "today --category='!school !private'|");
137     while(<SCHEDULE>) {
138         my ($begin, $end, $maxend);
139         next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
140         my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
141         $begin = $hs*60+$ms;
142         $maxend = $he*60+$me;
143         $end = ($begin)*3/4+($maxend)/4;
144         if($begin <= $now && $now < $end) {
145             $state{'schedule'} = $ev;
146             last;
147         } elsif ($state{'schedule'} eq $ev && $now > $maxend) {
148             $state{'schedule'} = '';
149         }
150     }
151     close(SCHEDULE);
152 }
153
154 sub do_hosts {
155     my @livehosts;
156     foreach my $host ("sarah", "homer", "lisa", "mona") {
157         push @livehosts, $host if(ping($host));
158     }
159     my $alive = "";
160     foreach my $host (@livehosts) {
161         $alive=$host unless(system(@ssh,$host, 'if pidof xscreensaver >/dev/null && DISPLAY=:0.0 xscreensaver-command -version >/dev/null 2>&1; then if DISPLAY=:0.0 xscreensaver-command -time 2>&1 |egrep "non-blanked|no saver status" >/dev/null; then true; else pid=`ps ax|egrep "[0-9]:[0-9][0-9] ssh marge .*(screen.*RR irc|Chat)"|sed -e "s/^  *//"|cut -d" " -f 1`; [ -f "$HOME/.irc.lock" -o -z "$pid" ] || kill $pid; false; fi; else false; fi') >> 8);
162     }
163     $state{'alive'} = $alive;
164 }
165
166 sub do_phone {
167     my $phone;
168     if(!ping('mona')) {
169         $phone="unknown";
170     } else {
171         my $last_slh=`@ssh mona cat .blue/last_slh 2>/dev/null`;
172         if(!$last_slh) {
173             $phone="unknown";
174         } elsif (time-$last_slh < 600) {
175             $phone="present";
176         } else {
177             $phone="absent";
178         }
179     }
180     if($state{'phone'} eq 'absent' && $phone eq 'present') {
181         custom_away("");
182     }
183 }
184
185 sub process_away {
186     if ($state{'customaway'}) {
187         $state{'away'}=$state{'customaway'};
188     } elsif ($state{'school'}) {
189         $state{'away'}="Class: ".$state{'school'};
190     } elsif ($state{'schedule'}) {
191         $state{'away'}=$state{'schedule'};
192     } elsif ($state{'alive'}) {
193         undef($state{'away'});
194     } elsif ($state{'phone'} eq 'absent') {
195         $state{'away'}="Out";
196     } elsif ($state{'phone'} ne 'absent') {
197         my @now=localtime;
198         if ($now[2] < 10) {
199             $state{'away'}="Sleeping";
200         } else {
201             $state{'away'}="Away from keyboard";
202         }
203     }
204 }
205
206 sub custom_away {
207     if($_[0]) {
208         $state{'customaway'} = $_[0];
209     } else {
210         undef $state{'customaway'};
211     }
212     process_away();
213     save();
214 }
215
216 sub custom_activity {
217     if($_[0]) {
218         $state{'activity'} = $_[0];
219     } else {
220         undef $state{'activity'};
221     }
222     save();
223 }
224
225 sub save {
226     if (defined($state{'activity'})) {
227         $state{'status'}=$state{'activity'};
228     } elsif (defined($state{'away'})) {
229         $state{'status'}=$state{'away'};
230     } elsif ($state{'alive'} eq "mona" || $state{'alive'} eq "homer") {
231         $state{'status'}="On desktop";
232     } elsif ($state{'alive'} eq "lisa") {
233         $state{'status'}="On laptop";
234     } elsif ($state{'alive'} eq "sarah") {
235         $state{'status'}="In bed";
236     }
237     open CONF, '>' . $ENV{'HOME'} . "/.away-tpope" || die $!;
238     foreach my $k (keys %state) {
239         my $val=$state{$k};
240         next unless($val);
241         $val =~ s/\n/\\n/g;
242         print CONF "$k=$val\n";
243     }
244     close CONF;
245     if(defined($state{'activity'})) {
246         open TMP, '>' . $ENV{'HOME'} . "/.activity";
247         print TMP $state{'activity'}."\n";
248         close TMP;
249     } else {
250         unlink($ENV{'HOME'} . "/.activity");
251     }
252     if(defined($state{'away'})) {
253         open TMP, '>' . $ENV{'HOME'} . "/.away";
254         print TMP $state{'away'}."\n";
255         close TMP;
256     } else {
257         unlink($ENV{'HOME'} . "/.away");
258     }
259     open TMP, '>' . $ENV{'HOME'} . "/.status";
260     print TMP $state{'status'}."\n";
261     close TMP;
262 }
263
264 sub load {
265     open CONF, '<' . $ENV{'HOME'} . "/.away-tpope" or return;
266     while(my $line=<CONF>) {
267         $line =~ s/\\n/\n/g;
268         $line =~ s/^([^=]*)=//;
269         chomp $line;
270         $state{$1}=$line;
271     }
272     close CONF;
273 }
274
275 sub quit_handler {
276     unlink $pipe;
277     unlink "/tmp/.away-tpope.pid";
278     save();
279     exit 0;
280 }
281
282 sub do_custom {
283     system("away-actions &");
284 }