view Ikariam.pm @ 23:54ab0becd730

implemented a prototype for collecting account status.
author "Rex Tsai <chihchun@kalug.linux.org.tw>"
date Thu, 09 Oct 2008 11:33:47 +0800
parents 9c52ae71c1cb
children d8117792c6f5
line wrap: on
line source

#!/usr/bin/env perl

use Class::DBI::AutoLoader (
    dsn       => 'dbi:SQLite:dbname=ikariam.sqlite',
    options   => { RaiseError => 1 },
    tables    => ['cities', 'island', 'user'],
    use_base  => 'Class::DBI::SQLite',
    namespace => 'Ikariam',
);

package Ikariam;

use Data::Dumper;
use LWP;
# use LWP::Debug qw(+ -conns -trace -debug);
use LWP::Debug qw(+trace);
use HTTP::Cookies;
use WWW::Mechanize;
use HTML::TagParser;
use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;

sub new
{
    my ($class, $server, $user, $pass) = @_;

    my $self =
    {
        mech => WWW::Mechanize->new(
            agent => "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092816 Iceweasel/3.0.1 (Debian-3.0.1-1)",
            timeout => 10,
        ),
        server => $server,
        user => $user,
        pass => $pass,
    };

    $self->{mech}->cookie_jar(HTTP::Cookies->new(file => "./cookies.txt", autosave => 1));
    $self->{mech}->default_headers->push_header('Accept-Encoding', 'deflate');
 
    return bless $self, $class;
}

sub viewScore
{
    my $self = shift;
    my $type = shift || 'score';
    my $user = shift || '';
    my $offset = shift || 0;

    my $res = $self->{mech}->post(sprintf("http://%s/index.php", $self->{server}), [
        highscoreType => $type,
        offset => $offset,
        searchUser => $user,
        view => 'highscore'
        ]);

    my $c;
    my $status = gunzip \$res->content => \$c 
        or die "gunzip failed: $GunzipError\n";

    my $html = HTML::TagParser->new($c);

    my ($table) = $html->getElementsByAttribute("class", "table01");
    my @elems = getElementsByTagName($table, "tr");

    my %users;
    foreach my $elem (@elems) {
        my $e;
        my %user;

        $e = getElementsByAttribute($elem, "class", "action");
        $e = getElementsByTagName($e, "a");

        if(defined ($e) && $e->getAttribute('href') =~ /index\.php\?view=sendMessage&with=(\d+)&oldView=highscore/)
        {
            $user{'id'} = $1;

            $e = getElementsByAttribute($elem, "class", "name");
            $user{'name'} = $e->innerText();

            $e = getElementsByAttribute($elem, "class", "allytag");
            $user{'ally'} = $e->innerText();

            $e  = getElementsByTagName($e, "a");
            if($e->getAttribute('href') =~ /\?view=allyPage&allyId=(\d+)/)
            {
                $user{'allyId'} = $1;
            }

            $e = getElementsByAttribute($elem, "class", "score");
            $user{$type} = $e->innerText();
            $user{$type} =~ s/,//g;

            $users{$user{'id'}} = \%user;
        } else {
            next;
        }
    }

    return \%users;
}

sub viewWorldMap
{
    my $self = shift;
    my $x = shift;
    my $y = shift;

    if(!defined($x) && !defined($y))
    {
        die('location required');
    }

    my $res = $self->{mech}->post(sprintf("http://%s/index.php?view=worldmap_iso", $self->{server}), [
            xajax => 'getMapData',
            'xajaxargs[]' => $x,
            'xajaxargs[]' => $y,
            xajaxr => time,
            ]);

    my $c;
    my $status = gunzip \$res->content => \$c 
        or die "gunzip failed: $GunzipError\n";

    my @islands;
    # parsing xjxobj
    while($c =~ /<cmd n="jc" t="addToMap"><xjxobj><e><k>0<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>1<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>2<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>3<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>4<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>5<\/k><v><!\[CDATA\[(\w+)\]\]><\/v><\/e><e><k>6<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><e><k>7<\/k><v><!\[CDATA\[(\d+)\]\]><\/v><\/e><\/xjxobj><\/cmd>/g)
    {
        my %island;
        $island{id} = $3;
        $island{x} = $1;
        $island{y} = $2;
        $island{name} = $6;
        $island{tradegood} = $4;
        $island{wonder} = $5;
        # $7 ?
        $island{people} = $8;
        push @islands, \%island;
    }
    return @islands;
}

sub viewHomeMap
{
    my $self = shift;

    my $res = $self->{mech}->get(sprintf("http://%s/index.php?view=worldmap_iso", $self->{server}));

    my $c;
    my $status = gunzip \$res->content => \$c 
        or die "gunzip failed: $GunzipError\n";

    # m[50][36]=new Array(564,1,5,'Risietia', '5', 13);
    # x = 43-57 = 6
    # y = 27-41 = 6
    my @islands;
    while($c =~ /m\[(\d+)\]\[(\d+)\]=new Array\((\d+),(\d+),(\d+),'(\w+)', '(\d+)', (\d+)\);/g)
    {
        my %island;
        $island{id} = $3;
        $island{x} = $1;
        $island{y} = $2;
        $island{name} = $6;
        $island{tradegood} = $4;
        $island{wonder} = $5;
        # $7 ?
        $island{people} = $8;

        #foreach my $i (sort(keys(%island)))
        #{
        #    printf ("%s %s\n", $i, $island{$i});
        #}
        #print("\n");
        push @islands, \%island;
    }
    return @islands;
}

sub viewIsland
{
    my $self = shift;
    my $island = shift;

    my $res = $self->{mech}->get(sprintf("http://%s/index.php?view=island&id=%s", $self->{server}, $island));

    my $c;
    my $status = gunzip \$res->content => \$c 
        or die "gunzip failed: $GunzipError\n";

    my $html = HTML::TagParser->new($c);

    # find inactivity and vacation
    my %status;
    foreach my $class (qw/inactivity vacation/)
    {
        @elems = $html->getElementsByAttribute("class", $class);
        foreach my $elem (@elems) {
            if($elem->innerText() =~ /^(.*?) \((\w)\)/) {
                $status{$1} = $2;
                # printf("%s\n", $elem->innerText());
            }
        }
    }

    # find content
    my @elems = $html->getElementsByClassName( "cityinfo" );
    my @cities;
    foreach my $elem (@elems) {
        my %info;

        my @e = getElementsByTagName($elem, "li");
        $info{'cityname'} = substr($e[0]->innerText(), 8);
        $info{'citylevel'} = substr($e[1]->innerText(), 14);
        $info{'owner'} = substr($e[2]->innerText(), 8);
        $info{'ally'} = substr($e[3]->innerText(), 8);
        delete($info{'ally'}) if($info{'ally'} eq '-');

        @e = getElementsByAttribute($elem, "class", "messageSend");
        if ( $e[0]->getAttribute("href") =~ /with=(\d+)&destinationCityId=(\d+)/)
        {
            $info{'user'} = $1;
            $info{'cityId'} = $2;
        }

        # update status;
        if(defined($status{$info{'cityname'}})) {
            $info{'status'} = $status{$info{'cityname'}};
        } else {
            $info{'status'} = undef;
        }
        # print(Dumper(\%info));
        push @cities, \%info;
    }

    return @cities;
}

sub check
{
    my $self = shift;

    my $aaaa = $self->{'cities'};

    # 運輸船
    # CargoShip 
    foreach my $i (keys(%{$self->{'cities'}})) 
    {

        # search for goods
        my $res = $self->{mech}->post(sprintf('http://%s/index.php', $self->{server}), [
            action => 'header',
            cityId => $i,
            function => 'changeCurrentCity',
            id => $i,
            oldView => 'city',
        ]);
        my $c;
        my $status = gunzip \$res->content => \$c 
            or die "gunzip failed: $GunzipError\n";

        my $html = HTML::TagParser->new($c);
        my @elems;

        foreach my $j (qw/value_wood value_wine value_marble value_crystal value_sulfur/) {
            my ($elem) = $html->getElementsByAttribute("id", $j);
            $self->{'cities'}->{$i}->{$j} = $elem->innerText();
            $self->{'cities'}->{$i}->{$j} =~ s/,//g;
        }

        my %types;
        $types{'army'} = [ qw/undef undef Slinger Swordsman Phalanx Ram Archer Catapult Gunsman Mortar SteamGiant Gyrocopter Bombardier Doctor Cook/ ];
        $types{'fleet'} = [ qw/undef undef Ram-Ship BallistaShip Flamethrower CatapultShip MortarShip PaddleWheelRam DivingBoat/ ];

        foreach my $x (qw/army fleet/)
        {
            $self->{'cities'}->{$i}->{$x} = {};
            # search army
            $res = $self->{mech}->get(sprintf('http://%s/index.php?view=cityMilitary-%s&id=%d', $self->{server}, $x, $i));
            $status = gunzip \$res->content => \$c 
                or die "gunzip failed: $GunzipError\n";

            $html = HTML::TagParser->new($c);
            @elems = $html->getElementsByTagName('td');

            foreach my $j (0..$#{$types{$x}}) {
                next if($types{$x}[$j] eq 'undef');
                if($elems[$j]->innerText() == '-') {
                    $self->{'cities'}->{$i}->{$x}->{$types{$x}[$j]} = 0;
                } else {
                    $self->{'cities'}->{$i}->{$x}->{$types{$x}[$j]} = $elems[$j]->innerText();
                }
            }
        }
    }
    print Dumper($self->{'cities'});
}

sub logout
{
    my $self = shift;
    $self->{mech}->get(sprintf('http://%s/index.php?action=loginAvatar&function=logout', $self->{server}));
}

sub login
{
    my $self = shift;

    my $res = $self->{mech}->post(sprintf("http://%s/index.php?action=loginAvatar&function=login", $self->{server}), [
        name => $self->{user},
        password => $self->{pass},
        ]);
    my $c;
    my $status = gunzip \$res->content => \$c 
        or die "gunzip failed: $GunzipError\n";

    if($c =~ /錯誤!/)
    {
        die ("password error\n");
    } else {
        my $html = HTML::TagParser->new($c);
        my @elems;
        
        @elems = $html->getElementsByAttribute("class", "avatarCities coords");
        foreach my $elem (@elems) {
            # my cities
            $self->{'cities'}->{$elem->getAttribute('value')} = {};
        }
    }
}

sub getElementsByTagName {
    my $element    = shift;
    my $tagname = lc(shift);
    my ( $flat, $cur ) = @$element;

    my $out = [];
    for( ; $cur <= $#$flat ; $cur++ ) {
        next if ( $flat->[$cur]->[001] ne $tagname );
        next if $flat->[$cur]->[000];                 # close
        my $elem = HTML::TagParser::Element->new( $flat, $cur );
        return $elem unless wantarray;
        push( @$out, $elem );
    }
    return unless wantarray;
    @$out;
}

sub getElementsByAttribute {
    my $element = shift;
    my $key  = lc(shift);
    my $val  = shift;
    my ( $flat, $cur ) = @$element;

    my $out  = [];
    for ( ; $cur <= $#$flat ; $cur++ ) {
        next if $flat->[$cur]->[000];    # close
        my $elem = HTML::TagParser::Element->new( $flat, $cur );
        my $attr = $elem->attributes();
        next unless exists $attr->{$key};
        next if ( $attr->{$key} ne $val );
        return $elem unless wantarray;
        push( @$out, $elem );
    }
    return unless wantarray;
    @$out;
}

1;