New away script
authorTim Pope <code@tpope.net>
Sun, 29 May 2005 02:59:00 +0000 (02:59 +0000)
committerTim Pope <code@tpope.net>
Sun, 29 May 2005 02:59:00 +0000 (02:59 +0000)
perl/away-tpope [new file with mode: 0755]

diff --git a/perl/away-tpope b/perl/away-tpope
new file mode 100755 (executable)
index 0000000..e5b7479
--- /dev/null
@@ -0,0 +1,284 @@
+#!/usr/bin/perl -w
+# $Id$
+# -*- perl -*- vim:set ft=perl sw=4 sts=4:
+
+use strict;
+use vars qw(%state @ssh $last $pipe);
+@ssh=("ssh","-a","-x","-oBatchmode=yes","-oSetupTimeOut=20");
+$pipe = "/tmp/.away-tpope";
+
+my $arg=shift || "";
+daemonize() if ($arg eq "-d");
+daemon() if ($arg eq "-D");
+
+
+if($arg eq "activity" || $arg eq "away") {
+    if(exists($ARGV[0])) {
+       die "Daemon not running\n" unless (-p $pipe);
+       my $a = join(" ",@ARGV);
+       $a =~ s/^-c$//;
+       open FIFO, ">$pipe";
+       print FIFO "$arg $a";
+    } else {
+       $arg =~ s/^away$/customaway/;
+       load();
+       print $state{$arg}."\n" if($state{$arg});
+    }
+} else {
+    exit 1;
+}
+
+sub daemon {
+    open TMP, ">/tmp/.away-tpope.pid";
+    print TMP "$$\n";
+    close TMP;
+    load();
+    $SIG{'INT'} = \&quit_handler;
+    $SIG{'TERM'} = \&quit_handler;
+    $SIG{'QUIT'} = \&quit_handler;
+    $SIG{'HUP'} = \&cycle;
+    die "Daemon already running\n" if (-r "/tmp/.tpope-away.pid");
+    unless (-p $pipe) {
+       unlink $pipe;
+       system("mkfifo", $pipe);
+    }
+    cycle();
+    $last=time;
+    while(1) {
+       main_loop();
+    }
+}
+
+sub daemonize {
+    chdir "/";
+    my $pid=fork();
+    if($pid) {
+       exit(0)
+    }
+    elsif(defined($pid)) {
+       daemon();
+    } else { exit(1); }
+}
+
+sub main_loop {
+    my $read="";
+    eval {
+       local $SIG{ALRM} = sub { die "alarm\n" };
+       alarm 1;
+       open FIFO, "<$pipe" and
+       $read = <FIFO>;
+       alarm 0;
+       close FIFO;
+    };
+    if($@) {
+       die unless $@ eq "alarm\n";
+       close FIFO;
+    }
+    if($read) {
+       chomp $read;
+       $read =~ s/^([a-z]*)( |$)//;
+       my $c = $1 || "";
+           if($c eq "activity") {
+               custom_activity($read);
+           } elsif($c eq "away") {
+               custom_away($read);
+           } else {
+               print "Unknown command: $c\n";
+           }
+    }
+    if(time-$last >=300) {
+       local $SIG{'HUP'} = "IGNORE";
+       cycle();
+       $last=time;
+    }
+}
+
+sub cycle {
+    do_schedule();
+    do_hosts();
+    do_phone();
+    process_away();
+    save();
+    do_custom();
+}
+
+sub ping {
+    open(SOUT, ">&STDOUT");
+    close(STDOUT);
+    my $ret=!(system('ping','-q','-c','1',shift)>>8);
+    open(STDOUT, ">&SOUT");
+    close(SOUT);
+    return $ret;
+}
+
+sub do_schedule {
+    my @now=localtime(time);
+    my $now=$now[3]*60+$now[2];
+
+    open(SCHEDULE, "today --category=school|");
+    while(<SCHEDULE>) {
+       my ($begin, $end, $maxend);
+       next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
+       my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
+       $begin = $hs*60+$ms-25;
+       $maxend = $he*60+$me+5;
+       $end = ($begin+25)*3/4+($maxend-5)/4;
+       if($begin <= $now && $now < $end) {
+           $state{'class'} = $ev;
+           last;
+       } elsif ($state{'class'} eq $ev && $now > $maxend) {
+           $state{'class'} = '';
+       }
+    }
+    close(SCHEDULE);
+
+
+    open(SCHEDULE, "today --category='!school !private'|");
+    while(<SCHEDULE>) {
+       my ($begin, $end, $maxend);
+       next unless /(\d\d):(\d\d)-(\d\d):(\d\d) (.*)/;
+       my ($hs,$ms,$he,$me,$ev) = ($1, $2, $3, $4, $5);
+       $begin = $hs*60+$ms;
+       $maxend = $he*60+$me;
+       $end = ($begin)*3/4+($maxend)/4;
+       if($begin <= $now && $now < $end) {
+           $state{'schedule'} = $ev;
+           last;
+       } elsif ($state{'schedule'} eq $ev && $now > $maxend) {
+           $state{'schedule'} = '';
+       }
+    }
+    close(SCHEDULE);
+}
+
+sub do_hosts {
+    my @livehosts;
+    foreach my $host ("sarah", "homer", "lisa", "mona") {
+       push @livehosts, $host if(ping($host));
+    }
+    my $alive = "";
+    foreach my $host (@livehosts) {
+       $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);
+    }
+    $state{'alive'} = $alive;
+}
+
+sub do_phone {
+    my $phone;
+    if(!ping('mona')) {
+       $phone="unknown";
+    } else {
+       my $last_slh=`@ssh mona cat .blue/last_slh 2>/dev/null`;
+       if(!$last_slh) {
+           $phone="unknown";
+       } elsif (time-$last_slh < 600) {
+           $phone="present";
+       } else {
+           $phone="absent";
+       }
+    }
+    if($state{'phone'} eq 'absent' && $phone eq 'present') {
+       custom_away("");
+    }
+}
+
+sub process_away {
+    if ($state{'customaway'}) {
+       $state{'away'}=$state{'customaway'};
+    } elsif ($state{'school'}) {
+       $state{'away'}="Class: ".$state{'school'};
+    } elsif ($state{'schedule'}) {
+       $state{'away'}=$state{'schedule'};
+    } elsif ($state{'alive'}) {
+       undef($state{'away'});
+    } elsif ($state{'phone'} eq 'absent') {
+       $state{'away'}="Out";
+    } elsif ($state{'phone'} ne 'absent') {
+       my @now=localtime;
+       if ($now[2] < 10) {
+           $state{'away'}="Sleeping";
+       } else {
+           $state{'away'}="Away from keyboard";
+       }
+    }
+}
+
+sub custom_away {
+    if($_[0]) {
+       $state{'customaway'} = $_[0];
+    } else {
+       undef $state{'customaway'};
+    }
+    process_away();
+    save();
+}
+
+sub custom_activity {
+    if($_[0]) {
+       $state{'activity'} = $_[0];
+    } else {
+       undef $state{'activity'};
+    }
+    save();
+}
+
+sub save {
+    if (defined($state{'activity'})) {
+       $state{'status'}=$state{'activity'};
+    } elsif (defined($state{'away'})) {
+       $state{'status'}=$state{'away'};
+    } elsif ($state{'alive'} eq "mona" || $state{'alive'} eq "homer") {
+       $state{'status'}="On desktop";
+    } elsif ($state{'alive'} eq "lisa") {
+       $state{'status'}="On laptop";
+    } elsif ($state{'alive'} eq "sarah") {
+       $state{'status'}="In bed";
+    }
+    open CONF, '>' . $ENV{'HOME'} . "/.away-tpope" || die $!;
+    foreach my $k (keys %state) {
+       my $val=$state{$k};
+       next unless($val);
+       $val =~ s/\n/\\n/g;
+       print CONF "$k=$val\n";
+    }
+    close CONF;
+    if(defined($state{'activity'})) {
+       open TMP, '>' . $ENV{'HOME'} . "/.activity";
+       print TMP $state{'activity'}."\n";
+       close TMP;
+    } else {
+       unlink($ENV{'HOME'} . "/.activity");
+    }
+    if(defined($state{'away'})) {
+       open TMP, '>' . $ENV{'HOME'} . "/.away";
+       print TMP $state{'away'}."\n";
+       close TMP;
+    } else {
+       unlink($ENV{'HOME'} . "/.away");
+    }
+    open TMP, '>' . $ENV{'HOME'} . "/.status";
+    print TMP $state{'status'}."\n";
+    close TMP;
+}
+
+sub load {
+    open CONF, '<' . $ENV{'HOME'} . "/.away-tpope" or return;
+    while(my $line=<CONF>) {
+       $line =~ s/\\n/\n/g;
+       $line =~ s/^([^=]*)=//;
+       chomp $line;
+       $state{$1}=$line;
+    }
+    close CONF;
+}
+
+sub quit_handler {
+    unlink $pipe;
+    unlink "/tmp/.away-tpope.pid";
+    save();
+    exit 0;
+}
+
+sub do_custom {
+    system("away-actions &");
+}