#! /usr/bin/perl -w my $version = "v1.1"; ############################################################################### # Monitors DGS for new games =head1 NAME dgsmon - Dragon Go Server Monitor =head1 SYNOPSIS dgsmon [-fchart] =head1 DESCRIPTION B monitors Dragon Go Server (http://www.dragongoserver.net) for you to check whether it's your turn to move on one or more games. For each game, it will print out the name of the opponent. =cut use warnings; use strict; use HTTP::Cookies; use LWP::UserAgent; use HTML::TreeBuilder; use HTML::Form; use Pod::Usage; ############################################################################### # Constants my $cookie_file = "$ENV{HOME}/.dgsmon.cookies"; my $config_file = "$ENV{HOME}/.dgsmonrc"; ############################################################################### # Variables my ($cookie_jar, $request, $response, $output_format); my (%config, $verbose); ############################################################################### =head1 OPTIONS =over 4 =item I<-fmon> Produce output in the format used by GKrellm FMonitor[1]. You can call B regularly with this option from a crontab, direct the output to a file, and set Gkrellm to display the information. A crontab entry that works every minute would look like: * * * * * $HOME/bin/dgsmon -fmon > $HOME/.dgsmon.output Then you would configure FMonitor to use $HOME/.dgsmon.output. =cut $output_format = 'NORMAL'; $verbose = 0; foreach my $arg (@ARGV) { if ($arg =~ /fmon/) { $output_format = 'FMON'; next; } if ($arg =~ /verbose/) { $verbose = 1; next; } pod2usage(-verbose=>2) if @ARGV; } =back =cut ############################################################################### # Lecture du fichier de config =head1 FILES =over 4 =item $HOME/.dgsmonrc Upon startup, the file $HOME/.dgsmonrc is checked for authentication information. This file is simple "key=value" file, one value per line, and must define a key C containing your user identifier, and a ke C containing your password. To prevent other users from accessing your login information, thie file must only be accessible by you. B will refuse to work if its access mode is not 600. Example: username=whiterabbit passwd=wonderland =cut { die "$config_file: Unable to read\n" unless -r $config_file; my $mode = (stat $config_file)[2]; die "$config_file must be 0600\n" if (($mode&0777) != 0600); open my $fh, $config_file or die "$config_file: $!\n"; while (<$fh>) { my ($key, $value) = /(\w+)=(.*)/; die "illegal config line: $_\n" unless defined $key and defined $value; $config{$key} = $value; } } ############################################################################### =item $HOME/.dgsmon.cookies This file is managed by B. It is used to store the login cookies that Dragon Go Server sends back, so B can just re-use them if possible. This saves authenticating yourself each time. To prevent other users from accessing your cookie information, which would let them connect using your account, this file must only be accessible by you. B will refuse to work if its access mode is not 600. =cut # Check if we've got cookies if (-e $cookie_file) { my $mode = (stat $cookie_file)[2]; die "$cookie_file must be 0600\n" if (($mode&0777) != 0600); $cookie_jar = HTTP::Cookies->new; $cookie_jar->load($cookie_file); die "$cookie_file: $!\n" unless defined $cookie_jar; } my $user_agent = LWP::UserAgent->new; # Try to log in with the current cookies $user_agent->cookie_jar($cookie_jar); $request = new HTTP::Request( GET => "http://www.dragongoserver.net/status.php" ); $response = $user_agent->request($request); my $logged_in = !($response->content =~ /have to be logged in/); ############################################################################### # Log in if required if (!$logged_in) { warn "loggin in...\n" if $verbose; # Log in $response = $user_agent->request( HTTP::Request->new( GET => "http://www.dragongoserver.net/index.php" )); die "Load login page failed: ".$response->status_line."\n" unless $response->is_success; warn "login page loaded\n" if $verbose; my $form = HTML::Form->parse($response); die "No form on login page\n" unless defined $form; $form->value("userid", $config{username}); $form->value("passwd", $config{passwd}); print "userid: ".$form->value("userid")."\n" if $verbose; print "passwd: ".$form->value("passwd")."\n" if $verbose; $request = $form->click("login"); die "clicking form failed\n" unless defined $request; warn "filled form\n" if $verbose; $response = $user_agent->request($request); warn "form response received\n" if $verbose; # Get login cookies $cookie_jar = HTTP::Cookies->new; $cookie_jar->extract_cookies($response); my $old_umask = umask(0077); $cookie_jar->save($cookie_file); umask($old_umask); $user_agent->cookie_jar($cookie_jar); warn "loading status page\n" if $verbose; # Finally, get the status page $request = new HTTP::Request( GET => $response->header('location') ); $response = $user_agent->request($request); } ############################################################################### # At this stage, $response should contain the main page # Get all the links containing 'game.php' my $tree = HTML::TreeBuilder->new(); $tree->parse($response->content); $tree->eof; my @out; foreach my $link ( $tree->look_down( # ! '_tag', 'a', sub { my $href = $_[0]->attr('href'); return unless defined $href; return unless $href =~ /game\.php/; } ) ) { push @out, $link; } if ($output_format eq 'FMON') { if (@out == 0) { print "none::\n"; } } foreach my $game (@out) { my @elems = $game->parent->parent->content_list; my ($board, $sgf ,$player, $color, $size, $handi, $komi) = map { $_->as_text; } @elems; $board =~ s/\D//g; if ($output_format eq 'FMON') { print "${player}::\n"; } else { print "[$board] $player ($size x $size, handi $handi komi $komi)\n"; } } =head1 LINKS [1] The FMonitor plugin is available from http://kmlinux.fjfi.cvut.cz/~makovick/gkrellm/index.html. =head1 HISTORY v1.1: Error message when login fails. v1.0: First release. Works for me(tm). =head1 LICENSE This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Yves "WhiteRabbit" Rutschle -- Challenge me on DGS! ;) Send comments and bug reports to dgs@rutschle.net. =cut