#!perl use LWP::Simple; use HTML::Parser; use HTML::TableExtract; use Sports::Baseball::Teams 0.33; use strict; use warnings; use subs 'speak'; my $team = get_team() || "sea"; my $content = LWP::Simple::get ("http://sports.espn.go.com/mlb/clubhouse?team=$team"); my ($html, $relevant); my $filter = new HTML::Parser ( start_h=>[\&main_handler,"'start',tagname,attr,text"], end_h =>[\&main_handler,"'end',tagname,attr,text"], text_h =>[sub {$html .= shift if $relevant},"text"], ); $filter->parse($content); my $linescore = new HTML::TableExtract(headers=> [qw(\bR\b \bH\b \bE\b)]); my $alldata = new HTML::TableExtract; $linescore->parse($html); $alldata->parse($html); #oh, I feel guilty about this one... (there's only one image tag, though) my ($image) = ($html =~ m!]+src="[^"]+/([\w]+\.gif)"!); #these are all kosher, though my ($awayscore,$homescore) = $linescore->rows(); my @scoretable = $alldata->table_state(1,0)->rows; my @upcoming = $alldata->table_state(0,2)->rows; my $details = ($alldata->table_state(0,2)->rows)[0]->[0]; my $status = $scoretable[0][0]; my $awayteam = Sports::Baseball::Teams->new_fromscore($scoretable[1][0]); my $hometeam = Sports::Baseball::Teams->new_fromscore($scoretable[2][0]); my ($ahead,$behind) = $$awayscore[0] > $$homescore[0] ? ($awayteam, $hometeam) : ($hometeam, $awayteam); my ($score, %RHE); if ($$awayscore[0] != $$homescore[0]) { $score = sprintf "%i to %i", sort {$b <=> $a } $$awayscore[0], $$homescore[0]; } else { $score = "tied at $$homescore[0]"; } $RHE{home} = sprintf "%s run%s, %s hit%s and %s error%s", map {($_ == 0 ? "no" : $_), ($_ != 1 ? "s" : "")} @$homescore; $RHE{away} = sprintf "%s run%s, %s hit%s and %s error%s", map {($_ == 0 ? "no" : $_), ($_ != 1 ? "s" : "")} @$awayscore; if ($status =~ /Final/) { my (%winner, %loser,%save); @winner{ qw(name w l) } = $details =~ /W: \s+ (\w+) \s+ \( (\d+) - (\d+) \) /x ; @loser{ qw(name w l) } = $details =~ /L: \s+ (\w+) \s+ \( (\d+) - (\d+) \) /x; @save{ qw(name s ) } = $details =~ /S: \s+ (\w+) \s+ \( (\d+) \) /x; speak qq% $awayteam at $hometeam, Final: $ahead defeats $behind, $score. The winning pitcher, $winner{'name'}, advances to $winner{'w'} and $winner{'l'}. The loser is $loser{'name'}, he falls to $loser{'w'} and $loser{'l'}. %, $save{'name'} ? "The save goes to $save{'name'}, giving him $save{'s'}." : "No save in the game.", " $hometeam finishes with $RHE{home}; $awayteam with $RHE{away}. ", "Next game:", getnextgame(@upcoming); } elsif ($status =~ /Delayed|Postponed/) { my $place = $hometeam->getlocation(); speak qq% Tonight's game between $hometeam and $awayteam has been $&, presumably due to crappy weather in $place. The next game may therefor not be what was expected. However, right now we think it will be %, getnextgame(@upcoming); } else { #in progress my (%awaypitcher,%homepitcher); $details =~ /Pitching: \s+ \w{2,3} \s+ - \s+ (\w+) \s* (?: \( ( [^)]+ ) \) )? \s+ \w{2,3} \s+ - \s+ (\w+) \s* (?: \( ( [^)]+ ) \) )? /sx; @awaypitcher{'name','when'} = ($1,$2); @homepitcher{'name','when'} = ($3,$4); my $report = "In progress, $awayteam at $hometeam: "; if ($status =~ /top|bot/i) { $report .= "with " . get_situation($image); } $report .= get_inning($status) . ", "; if ($$awayscore[0] == $$homescore[0]) {$report .= "$score.\n";} else {$report .= "$ahead leads $behind, $score.\n";} $report .= "The current pitcher for $hometeam is $homepitcher{name}"; if ( $homepitcher{when} && $homepitcher{when} !~ /-/ ) { $report .= ", who relieved in the $homepitcher{when}"; } $report .= "; for $awayteam, $awaypitcher{name}"; if ( $awaypitcher{when} && $awaypitcher{when} !~ /-/ ) { $report .= ", who relieved in the $awaypitcher{when}"; } $report .= ". $hometeam has $RHE{home}; $awayteam has $RHE{away}."; speak $report; } ##End of main code block. sub get_team { my $input; if (@ARGV) { $input = $ARGV[0]; } else { return unless $^X eq 'MacPerl'; require MacPerl; $input = MacPerl::Ask("What team would you like the score for?"); $input =~ s/^\s*//; $input =~ s/\s*$//; } my $team = Sports::Baseball::Teams->new($input) || Sports::Baseball::Teams->new_fromscore($input); if ($input && !$team) {die "Unable to resolve a team from '$input'\n";} return $team ? $team->getkey() : undef; } sub main_handler { my ($whichend,$tagname,$attr,$text) = @_; if ($tagname eq "div") { if ($whichend eq 'start'){ $relevant = $$attr{id} && ($$attr{id} eq "topstory" || $$attr{id} eq "schedOne"); } else { $html .= $text if $relevant; #in case we need the output to be valid HTML $relevant = 0; } } $html .= $text if $relevant; } sub speak { if ($^X eq 'MacPerl') { require MacPerl; MacPerl::DoAppleScript (qq!say "@_"!); } elsif ($^O eq 'darwin') { system 'osascript', '-e',qq(say "@_"); } else { print map {"$_\n"} @_; } } sub get_inning { # e.g. "in the middle of the 8th inning" my ($when, $inning) = shift =~ /(Top|Mid|Bot|End)\D*(\d+\w+)/i; $when =~ s/Mid/Middle/i; $when =~ s/Bot/Bottom/i; #return : if ($when eq "End") {"at the end of the $inning inning"} elsif ($when eq "Middle") {$inning !~ /^7/ ? "in the middle of the $inning inning" : "at the 7th inning stretch"} else {"in the $when of the $inning inning "} } sub get_situation { my ($outs, $on1st, $on2nd, $on3rd) = shift =~ /^(\d)(\d)(\d)(\d)/; if ($& == 0) {return "none on and none out "} #VEEEERY special case :-) my $menon = $on1st + $on2nd + $on3rd; my ($bases, $situation); if ($menon == 1) { $bases = "a runner on "; if ($on1st) {$bases .= "1st"} elsif ($on2nd) {$bases .= "2nd"} else {$bases .= "3rd"} } elsif ($menon == 2) { $bases = "runners on "; if (!$on1st) {$bases .= "second and third"} elsif (!$on2nd) {$bases .= "the corners"} elsif (!$on3rd) {$bases .= "first and second"} } elsif ($menon == 3) {$bases = "the bases loaded"} else {$bases = "the bases empty"} return "$bases and " . ($outs ? "$outs out" : "none out") . " "; } sub getnextgame { my ($date,$opponent,$time) = @{$_[2]}[0..3]; my $matchup = $_[3][0]; my ($daypat, $weekday); my (@away, @home, $awayteam, $hometeam); my ($homename, $awayname, $awayrecord, $homerecord); if ($opponent =~ /^\@/) { substr($opponent,0,1) = "at "} else { substr($opponent,0,0) = "versus "} ($daypat) = $date =~ /^(\w+)/; for (qw (Monday Tuesday Wednesday Thursday Friday Saturday Sunday)) {$weekday = $_ if /$daypat/} (@away[0..2],@home[0..2]) = $matchup =~ /(\w{2,3}) - (\w+) (?:\((\d+-\d+)\))?/g; $hometeam = Sports::Baseball::Teams->new($home[0]); $awayteam = Sports::Baseball::Teams->new($away[0]); $homename = $hometeam->getplacename(); $awayname = $awayteam->getplacename(); $homerecord = sprintf "%i and %i", $home[2] =~ /(\d+)/g; $awayrecord = sprintf "%i and %i", $away[2] =~ /(\d+)/g; return qq% $weekday, $time Eastern Time, $opponent. Expected matchup: for $awayname, $away[1] ($awayrecord); for $homename, $home[1] ($homerecord). %; }