#!/usr/bin/perl -w use LWP::Simple; # start! I wouldn't copy use HTML::TreeBuilder; # anything seen in here, use HTML::Entities; # it's for your own good use DBI; use DBD::SQLite; use strict; ### ### #####D#O#N#E############### # # # main hash and array # # # ########################### ### ### my %things = ( 'help' => sub { print curti(), " - Commands: create, parse, find, time, exit, report\n" }, 'find' => \&sql_finder, 'parse' => \&sql_writer, 'create' => \&sql_create, 'time' => sub { print curti(), " There you go.\n" }, 'report' => sub { my @rofl = split(/\s+/,shift); hash_report() and return if $rofl[0] =~ /hash/i; get_meta('reset',$rofl[1]) and return if $rofl[0] =~ /reset/i; meta_report() and return if $rofl[0] =~ /meta/i; print curti(), " Usage: report [ hash | meta | reset [ hh:mm:ss ] ]\n"; } ); my @fields = ( 'number', 'series', # why is @fields out here? 'season', # well billy, I just don't 'station', # know~! :) :P ;) xD <3 :3 'company', 'airtime', 'eta', # it's only used once, may 'eps', # as well ditch it soon :) 'links' ); ### ### #####D#O#N#E################ # # # heart of the progrum # # # ############################ ### ### my $subjex = intro(); while($subjex !~ /exit/io){ foreach my $regex (sort keys %things) { if($subjex =~ /^\s*($regex)(?:\s*(.*))?/i) { $things{$1}->($2); } } print curti(), " > "; chomp($subjex = ); } ### ### #####D#O#N#E############## # # # spiffy subroutines # # # ########################## ### ### #D#O#N#E# # # # # # # # # # stuff for anidb links # # # # # # # # # # # # # # sub anidb_link { # makes pretty URLs my $url = shift; return '-' if !$url; $url = 'http://anidb.info/a' . $url; return $url; } sub anidb_id { # return AIDs my $url = shift; $url =~ s/^http.*aid.(\d+)$/$1/i; return $url; } #D#O#N#E# # # # # # # # sql stuff... ugh! # # # # # # # # # # # # sub build_hash { my %showtime = {}; my $dbh = DBI->connect( 'dbi:SQLite:dbname=mahou.db',undef,undef, { RaiseError => 1, AutoCommit => 0 } ); eval { # build hash from mahou_data... # someone should code this. ;-) }; if($@) { print curti(), " Build hash: Database error.\n"; } $dbh->disconnect; return 0; } sub check_meta { my $pop = pop; my @delta = parti($pop); print curti(), " \u$_[0]: Check meta: Checking: @delta\n"; # first: same hour & no more than thirty minutes # second: one hour & no less than thirty minutes if (($delta[0] == 0 and $delta[1] < 30 and $delta[1] >= 0 ) or ($delta[0] == 1 and $delta[1] < -30 and $delta[1] <= 0 )) { print curti(), " \u$_[0]: Check meta: Miss!\n"; return @{[ 0,$_[1],++$_[2],$pop ]}; } else { print curti(), " \u$_[0]: Check meta: Hit!\n"; return @{[ 1,++$_[1],$_[2],curti() ]}; } return curti(), " \u$_[0]: Check meta: Erroneous check: Replace coder.\n"; } sub get_meta { # create db if it doesn't exist sql_create() unless -e 'mahou.db'; # hash of params for the check my %type = ( 'query' => { 'return' => 0, 'update' => 1, 'reset' => 0 }, 'report' => { 'return' => 1, 'update' => 0, 'reset' => 0 }, 'reset' => { 'return' => 1, 'update' => 0, 'reset' => 1 } ); # this wasn't completely needed; # it's here for scalability? ^^; my $dbh = DBI->connect( 'dbi:SQLite:dbname=mahou.db',undef,undef, { RaiseError => 1, AutoCommit => 0 } ); # check who sent for us my ($from,%return) = (shift,{}); print curti(), " \u$from: Get meta: Using method:"; foreach my $lol (keys %{$type{$from}}) { print " $lol" if $type{$from}->{$lol}; } print "\n"; eval { if ($type{$from}->{'reset'}) { $dbh->do('DELETE FROM meta_data'); $dbh->do("INSERT INTO meta_data VALUES(0,0,'@{[ $_[0] ? $_[0] : q(12:00:00) ]}')"); $dbh->commit; $return{'new'} = $_[0] ? shift : '12:00:00'; } # get meta from db my @old = $dbh->selectrow_array('SELECT * from meta_data'); print curti(), " \u$from: Get meta: Data from table: @old\n"; if ($type{$from}->{'update'}) { # check and get new meta my ($hit,@new) = check_meta($from,@old); print curti(), " \u$from: Get meta: Removing old meta, inserting new meta.\n"; # delete old meta $dbh->do('DELETE FROM meta_data'); # insert and commit $dbh->do("INSERT INTO meta_data values($new[0],$new[1],'$new[2]')"); $dbh->commit; $return{'new'} = [ @new ]; $return{'hit'} = $hit; print curti(), " \u$from: Get meta: Meta updated, new meta: @{$return{'new'}}\n"; } $return{'old'} = [ @old ]; }; if($@) { print curti(), " \u$from: Get meta: Database error.\n"; $dbh->rollback; } $dbh->disconnect; # only send back what we need return $type{$from}->{'reset'} ? $return{'new'} : \@{$return{'old'}} if $type{$from}->{'return'}; return $return{'hit'}; } #D#O#N#E# # # # # # # # # # # # # # # core sql search/writer thingies # # # # # # # # # # # # # # # # # # # sub sql_finder { # set need stuff, check metadata my $search = shift; my @data; sql_writer() if get_meta('query'); # connect to db, finally my $dbh = DBI->connect( 'dbi:SQLite:dbname=mahou.db',undef,undef, { RaiseError => 1, AutoCommit => 0 } ); eval { print curti(), " Sql finder: Starting search...\n"; # search, first checks for aid, then defaults to name if($search =~ /^(?:ID:)?(\d+)$/) { @data = $dbh->selectrow_array("SELECT * from mahou_data WHERE aid = $1"); } else { $search = '%' . $search . '%'; my $sth = $dbh->prepare('SELECT * from mahou_data WHERE name like ?'); $sth->execute($search); @data = $sth->fetchrow_array; } }; if($@) { print curti(), " Sql finder: Database error.\n"; } $dbh->disconnect; if(@data) { print curti(), " Sql finder: Adjusting time from: $data[4]\n"; $data[4] = fixti($data[4]); print curti(), " Query Data:\n"; print " $_\n" foreach @data; } else { print curti(), " Query Data: No results.\n"; } } sub sql_writer { print curti(), " Sql writer: Getting hash...\n"; # grab hash from parser and connect to db my $hash = core_parse(); print curti(), " Sql writer: Connecting and preparing insert.\n"; my $dbh = DBI->connect( 'dbi:SQLite:dbname=mahou.db',undef,undef, { RaiseError => 1, AutoCommit => 0 } ); eval { # clear table, prepare insert $dbh->do('DELETE FROM mahou_data'); my $sth = $dbh->prepare('INSERT INTO mahou_data values(?,?,?,?,?)'); # insert print curti(), " Sql writer: Inserting rows.\n"; foreach my $anime (sort keys %{$hash}) { $sth->execute($anime,@{[ map { $hash->{$anime}{$_} } ('link','station','airtime','eta') ]}); } $dbh->commit; }; if($@) { $dbh->rollback; print curti(), " Sql writer: Database error.\n"; } $dbh->disconnect; print curti(), " Sql writer: Database updated.\n"; } sub sql_create { print curti(), " Please create database file (mahou.db).\n"; exit 0; } #D#O#N#E# # # # # # # # # # # # # download, parse and related # # # # # # # # # # # # # # # # # sub table_header_has { my ($table, $str) = @_; return defined $table->look_down( '_tag', 'th', sub { $_[0]->as_text() eq $str } ); } # by pc486 sub core_parse { # build tree, get shit from it ^-^ print curti(), " Core parse: Starting parser...\n"; my ($html,%showtime) = (get("http://mahou.org/Showtime"),{}); $html = HTML::TreeBuilder->new_from_content($html); # first step: find time table print curti(), " Core parse: Searching for table...\n"; my $table = $html->look_down( '_tag', 'table', 'bgcolor', '#bfa89b', sub { table_header_has($_[0], "ETA") }, sub { not table_header_has($_[0], "Starting") } ); # by pc486 # second step: find ACTUAL time table print curti(), " Core parse: Constructing hash...\n"; foreach my $anime ($table->look_down( '_tag', 'tr', 'align', undef, # skip first 'bgcolor', '#e9dfdb') ){ # first: get the name, for LINK: stays as hash; pc486! # second: get the ^^ link (which is kinda good) my $name = $anime->look_down('_tag', 'a', 'name', qr/\d+/)->parent(); my $link = $name->parent()->look_down('_tag', 'a', 'href', qr/anidb/); # starts the hash (with an aid), then populates it with map $showtime{$name->as_text}->{'link'} = $link ? anidb_id($link->{'href'}) : undef; map { $showtime{$name->as_text}->{$fields[$_]} = @{[ $anime->look_down('_tag', 'td') ]}[$_]->as_text } qw(3 5 6); } $html->delete; print curti(), " Core parse: Returning hash to parent...\n"; return \%showtime; } #D#O#N#E# # # # # # # # # stuff for the idler # # # # # # # # # # # # # sub meta_report { print curti(), " Meta report: Initializing...\n"; my $meta = get_meta('report'); print curti(), " Mahou Meta Report:\n", " re-parse hits -> @{[ $meta->[0] ]}\n", " re-parse misses -> @{[ $meta->[1] ]}\n", " time since last -> @{[ cheti(parti($meta->[2])) ]}\n"; } sub intro { print ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n", "::::::::: :::::::::\n", "::::::::: Welcome to the Mahou.org/Showtime parser! :::::::::\n", "::::::::: :::::::::\n", "::::::::: `Being dumb since 1823!` :::::::::\n", "::::::::: `Or something.` :::::::::::::::::::\n", "::::::::: ::By: Dean_Kreger::\n", ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n"; return('help'); } # I may do this if I get bored... sub hash_report { my %showtime = {}; my $lol = build_hash(); # returns zero if ($lol) { foreach my $anime (sort keys %showtime) { print "$anime <-<+>-> ", anidb_link($showtime{$anime}->{'link'}), "\n Station -> ", $showtime{$anime}->{'station'}, "\n Airtime -> ", $showtime{$anime}->{'airtime'}, "\n ETA -> ", $showtime{$anime}->{'eta'}, "\n\n"; } } print curti(), " The hash report function has yet to be implemented.\n"; } #D#O#N#E# # # # # # # # # # # # # # cute time related subroutines # # # # # # # # # # # # # # # # # # sub fixti { my @parti = (0,parti(@{get_meta('report')}->[2])); print curti(), " Fixti: \@parti: @parti\n"; return shift unless $parti[2]; my %fixti = (map { $_ => 0 - shift @parti } qw(d h m s)); print curti(), " Fixti: \%fixti:"; print " $fixti{$_}" foreach sort keys %fixti; print "\n"; map { $fixti{chop($_)} += $_ } @{[ split(/\s+/,shift) ]}; ($fixti{'d'},$fixti{'h'},$fixti{'m'},$fixti{'s'}) = # better way to do this? fucti($fixti{'d'},$fixti{'h'},$fixti{'m'},$fixti{'s'}); return "@{[ map { qq($fixti{$_}$_) } qw(d h m)]}"; } # fixes the time... DEAN'S WAY! sub cheti { my $conti = shift; return "update already! it's been like a day or more" if $conti < 0; return "more than five hours... I think..." if $conti > 5; return "like... more than an hour, at least" if $conti > 1; return "pretty much less than an hour" if $conti <= 1; return "erroneous check; please replace coder"; } # checks the time... DEAN'S WAY! ... Sorta... sub parti { my @parti; push(@parti,shift); my @curti; push(@curti,curti()); print curti(), " Parti: Doing: @curti - @parti\n"; map { @$_ = split(/:/,$_->[0]) } (\@parti,\@curti); return @{[ map { $curti[$_] - $parti[$_] } qw(0 1 2) ]}; } # parses the time... DEAN'S WAY! sub fucti { my @time = @_; print curti(), " Fixti: Fucti: Old \@time: @time\n"; --$time[2] and $time[3] += 60 if $time[3] < 0; ++$time[2] and $time[3] -= 60 if $time[3] > 60; --$time[1] and $time[2] += 60 if $time[2] < 0; ++$time[1] and $time[2] -= 60 if $time[2] > 60; --$time[0] and $time[1] += 24 if $time[1] < 0; ++$time[0] and $time[1] -= 24 if $time[1] > 24; $time[0] += 7 if $time[0] < 0; print curti(), " Fixti: Fucti: New \@time: @time\n"; pop @time; return @time; } # fuck time... ... ... DEAN'S WAY! sub curti { my @time = @{[ gmtime ]}[2,1,0]; $time[0] = $time[0]-3 < 0 ? $time[0]+9 : $time[0]-3; return sprintf('%02d:%02d:%02d', @time); } # gets the time... DEAN'S WAY!