Perl Bot

Perl bot designed to play Khan Wars web based game.

 

 

Features:
- Builder – Build all buildings constantly, calculating the resource production per hour to perform minimal login attempts.
- Make multiple attacks with chosen army and delay of time
- Make Army
- Captcha hacked
- VIP mode not needed.
- Voting to earn additional resources
- Ban protection

Download

Usage: 

Open bot.pl file and fill your details (username, password, world) in the Config section of the script.

 

#!/usr/bin/perl

use warnings;
use strict;

use Data::Dumper;
use KhanBot;

############################################
# Config  - Fill your details here         #
############################################
my $loginUsername = 'someuser';
my $loginPassword = 'secret';
my $loginWorld = 5;
my $baseUrl = 'http://45.game.hanovete.com';
############################################
############################################

$| = 1;

# CONFIG 
our $bot = KhanBot->new({
    'baseUrl' => $baseUrl,
    'loginWorld' => $loginWorld,
    'loginUsername' => $loginUsername,
    'loginPassword' => $loginPassword,
});

$bot->login();
print `date` . "logged!\n\n";

# Builder Example
&buildNewTown(64,110);
exit;

# print Dumper($bot->getData());
# exit;

# Attack Example
=pod
sleep 4 * 60 * 60;
$bot->attack(67,113);
exit;
=cut

# Change Town Example
=pod
print Dumper($bot->goCamp(345));
print Dumper($bot->gbackToMain(354));
exit;
=cut

########
# Subs #
########

sub buildNewTown
{
    my $x = shift;
    my $y = shift;
    my $i = 6;
    while (1) {
        $i++;

        $bot->doVoting();
        $bot->makeBuilding(1) if ($i <= 30);
        $bot->makeBuilding(2) if ($i <= 30);
        $bot->makeBuilding(3) if ($i <= 30);
        $bot->makeBuilding(4) if ($i <= 30);
        $bot->makeBuilding(5) if ($i <= 30);
        $bot->makeBuilding(15) if ($i == 1 || $i >= 10);

        # 10 LEVEL
        if ($i == 10) {
            # storage 10 level (1+9)       
            foreach(1..9) {
                $bot->makeBuilding(15);
            }  

            # market 3 level
            foreach (1..3) {
                $bot->makeBuilding(9);
            }

            # blacksmith 2 level
            foreach (1..2) {
                $bot->makeBuilding(10);
            }

            # baracks 5 level
            foreach (1..5) {
                $bot->makeBuilding(6);
            }

            # blacksmith 5 level (2+3)
            foreach (1..3) {
                $bot->makeBuilding(10);
            }

            # stables 1 level
            $bot->makeBuilding(7);
        }

        # 20 LEVEL
        if ($i == 20) {
            # blacksmith 10 level (2+8)
            foreach (1..8) {
                $bot->makeBuilding(10);
            }

            # workshop 1 leve;
            $bot->makeBuilding(8);
        }

        # 25 LEVEL
        if ($i == 25) {
            # stables 11 level (1+10)
            foreach (1..10) {
                $bot->makeBuilding(7);
            }

            # workshop 15 level (1 + 14)
            foreach (1..14) {
                $bot->makeBuilding(8);
            }

            # baracks 10 level (5 + 5)
            foreach (1..5) {
                $bot->makeBuilding(6);
            }

            # Havy Cavalery done!
            # Crossbow Archers done!
            # Ballista Done !
        }

        # 30 LEVEL
        if ($i == 30) {
            # stables 15 level (1+10+4)
            foreach (1..4) {
                $bot->makeBuilding(7);
            }

            # baraks 20 level (5+5+10)
            foreach (1..10) {
                $bot->makeBuilding(6);
            }

            # blacksmith 17 level (2+8+7)
            foreach (1..7) {
                $bot->makeBuilding(10);
            }

            # inf 5 level
            foreach (1..5) {
                $bot->makeBuilding(11);
            }

            # wall 20 level
            foreach (1..20) {
                $bot->makeBuilding(12);
            }

            # tower 10 level
            foreach (1..10) {
                $bot->makeBuilding(16);
            }
        }

        if ($i >= 30) {
            &makeArmy();
            $bot->logout();
            sleep 2 * 60 * 60;

            &makeArmy2();
            $bot->logout();
            sleep 4 * 60 * 60;

            &makeArmy3();
            $bot->logout();
            sleep 6 * 60 * 60;
        }

        $bot->banProtection();
    }
}

sub makeArmy
{
    sleep 60;
    $bot->doVoting();
    sleep 60;
    print `date` . "make army ";
    print Dumper($bot->makeMaxUnits(
        {'id' => 11, 'value' => 'max', 'ratio' => 0.5},
        {'id' => 13, 'value' => 'max', 'ratio' => 0.3},
        {'id' => 18, 'value' => 'max', 'ratio' => 1},
    ));

    my $data = $bot->getData();
    print "\nRevision!! -- \n";
    print Dumper($data->{resources});
    print "\n\nTime " . $data->{onlineTime} . " hours !!\n\n";
    print "\n\nRank " . $data->{myRank} . "\n\n";

    $bot->logout();

    if ($data->{onlineTime} >= 19) {
        die ('onlineTime' . $data->{onlineTime});
    }

    print `date` . "loggedout!\n\n";
}

sub makeArmy2
{
    sleep 60;
    $bot->doVoting();
    sleep 60;
    print `date` . "make army ";
    print Dumper($bot->makeMaxUnits(
        {'id' => 1, 'value' => 'max', 'ratio' => 0.1},
        {'id' => 2, 'value' => 'max', 'ratio' => 0.2},
        {'id' => 3, 'value' => 'max', 'ratio' => 0.3},
        {'id' => 4, 'value' => 'max', 'ratio' => 0.4},
        {'id' => 10, 'value' => 'max', 'ratio' => 0.5},
        {'id' => 9, 'value' => 'max', 'ratio' => 0.6},
        {'id' => 16, 'value' => 'max', 'ratio' => 0.7},
        {'id' => 17, 'value' => 'max', 'ratio' => 0.8},
        {'id' => 19, 'value' => 'max', 'ratio' => 1},
    ));

    my $data = $bot->getData();
    print "\nRevision!! -- \n";
    print Dumper($data->{resources});
    print "\n\nTime " . $data->{onlineTime} . " hours !!\n\n";
    print "\n\nRank " . $data->{myRank} . "\n\n";

    $bot->logout();

    if ($data->{onlineTime} >= 19) {
        die ('onlineTime' . $data->{onlineTime});
    }

    print `date` . "loggedout!\n\n";
}

sub makeArmy3
{
    sleep 60;
    $bot->doVoting();
    sleep 60;
    print `date` . "make army ";
    print Dumper($bot->makeMaxUnits(
        {'id' => 20, 'value' => 'max', 'ratio' => 1},
        {'id' => 21, 'value' => 'max', 'ratio' => 1},
    ));

    my $data = $bot->getData();
    print "\nRevision!! -- \n";
    print Dumper($data->{resources});
    print "\n\nTime " . $data->{onlineTime} . " hours !!\n\n";
    print "\n\nRank " . $data->{myRank} . "\n\n";

    $bot->logout();

    if ($data->{onlineTime} >= 19) {
        die ('onlineTime' . $data->{onlineTime});
    }

    print `date` . "loggedout!\n\n";
}

sub attack
{    
    my $x = shift;
    my $y = shift;
    my $xx = shift || 64;
    my $yy = shift || 111;
    my $type = shift || 3;

    $bot->sendMarch({
        'fromX' => $xx,    
        'fromY' => $yy,
        'targetX' => $x,
        'targetY' => $y,
        'add_hero' => 1,
        'speed' => 10,
        'army_orders' => 504,
        'type' => $type,
        'units' => 'all',
    });

    $bot->logout();
}

 

KhanBot.pm

package KhanBot;

use warnings;
use strict;

use WWW::Mechanize;
use HTTP::Cookies;
use HTTP::Request;
use URI::Escape;
use POSIX qw/ceil/;
use JSON;

use Data::Dumper;

sub new {
    my $proto = shift;
    my $params = shift;
    my $class = ref($proto) || $proto;
    my $self  = {};

    # buildings names
    $self->{buildingIdToName}->{1} = 'goldMine';
    $self->{buildingIdToName}->{2} = 'ironMine';
    $self->{buildingIdToName}->{3} = 'woodMine';
    $self->{buildingIdToName}->{4} = 'foodMine';
    $self->{buildingIdToName}->{5} = 'home';
    $self->{buildingIdToName}->{6} = 'barrack';
    $self->{buildingIdToName}->{7} = 'stable';
    $self->{buildingIdToName}->{8} = 'workShop';
    $self->{buildingIdToName}->{9} = 'market';
    $self->{buildingIdToName}->{10} = 'mithy';
    $self->{buildingIdToName}->{11} = 'lazaret';
    $self->{buildingIdToName}->{12} = 'wall';
    $self->{buildingIdToName}->{13} = 'order';
    $self->{buildingIdToName}->{14} = 'stash';
    $self->{buildingIdToName}->{15} = 'storage';
    $self->{buildingIdToName}->{16} = 'tower';

    # buildings max level
    $self->{buildingMaxLevel}->{1} = 30;
    $self->{buildingMaxLevel}->{2} = 30;
    $self->{buildingMaxLevel}->{3} = 30;
    $self->{buildingMaxLevel}->{4} = 30;
    $self->{buildingMaxLevel}->{5} = 30;
    $self->{buildingMaxLevel}->{6} = 25;
    $self->{buildingMaxLevel}->{7} = 20;
    $self->{buildingMaxLevel}->{8} = 15;
    $self->{buildingMaxLevel}->{9} = 25;
    $self->{buildingMaxLevel}->{10} = 20;
    $self->{buildingMaxLevel}->{11} = 10;
    $self->{buildingMaxLevel}->{12} = 20;
    $self->{buildingMaxLevel}->{13} = 4;
    $self->{buildingMaxLevel}->{14} = 20;
    $self->{buildingMaxLevel}->{15} = 30;
    $self->{buildingMaxLevel}->{16} = 10;

    # unit names
    $self->{unitNames}->{1} = 'Pikeman';
    $self->{unitNames}->{2} = 'Swordsman';
    $self->{unitNames}->{3} = 'Axeman';
    $self->{unitNames}->{4} = 'Maceman';
    $self->{unitNames}->{16} = 'shortArcher';
    $self->{unitNames}->{17} = 'longArcher';
    $self->{unitNames}->{18} = 'crossArcher';

    $self->{unitNames}->{9} = 'quickWalker';
    $self->{unitNames}->{10} = 'lightCavalr';
    $self->{unitNames}->{11} = 'heavyCavalry';
    $self->{unitNames}->{19} = 'archerCavalry';

    $self->{unitNames}->{20} = 'Nobleman';
    $self->{unitNames}->{21} = 'tangraPriest';

    $self->{unitNames}->{12} = 'Ram';
    $self->{unitNames}->{13} = 'Ballistician';
    $self->{unitNames}->{14} = 'Catapult';
    $self->{unitNames}->{15} = 'Trebuchet';

    # unit types
    $self->{unitType}->{1} = 1;
    $self->{unitType}->{2} = 1;
    $self->{unitType}->{3} = 1;
    $self->{unitType}->{4} = 1;
    $self->{unitType}->{16} = 1;
    $self->{unitType}->{17} = 1;
    $self->{unitType}->{18} = 1;

    $self->{unitType}->{9} = 2;
    $self->{unitType}->{10} = 2;
    $self->{unitType}->{11} = 2;
    $self->{unitType}->{19} = 2;

    $self->{unitType}->{20} = 3;
    $self->{unitType}->{21} = 3;

    $self->{unitType}->{12} = 4;
    $self->{unitType}->{13} = 4;
    $self->{unitType}->{14} = 4;
    $self->{unitType}->{15} = 4;

    # params
    $self->{cookieFile} = $params->{cookieFile} || 'cookies.txt';
    $self->{mechAutosave} = $params->{mechAutosave} || 1;
    $self->{mechiIgnoreDiscard} = $params->{mechiIgnoreDiscard} || 1;
    $self->{userAgent} = $params->{userAgent} || 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)';
    $self->{loginUrl} = $params->{loginUrl} || "http://www.hanovete.com/login.php";
    $self->{baseUrl} = $params->{baseUrl} || "http://45.game.hanovete.com";
    $self->{homeUrl} = $params->{homeUrl} || $self->{baseUrl} . "/overview.php";
    $self->{capOkUrl} = $self->{baseUrl} . "/map.php";
    $self->{buildUrl} = $self->{baseUrl} . "/buildings.php";
    $self->{premResUrl} = $self->{baseUrl} . "/premium_res.php";
    $self->{barracksUrl} = $self->{baseUrl} . "/barracks.php";
    $self->{logoutUrl} = $self->{baseUrl} . "/logout.php";
    $self->{barracksUrl1} = $self->{baseUrl} . "/barracks.php?unit_type=1";
    $self->{barracksUrl2} = $self->{baseUrl} . "/barracks.php?unit_type=2";
    $self->{barracksUrl3} = $self->{baseUrl} . "/barracks.php?unit_type=3";
    $self->{barracksUrl4} = $self->{baseUrl} . "/barracks.php?unit_type=4";
    $self->{capPrefix} = $params->{capPrefix} || "submit";
    $self->{capSizeX} = $params->{capSizeX} || 100;
    $self->{capSizeY} = $params->{capSizeY} || 50;
    $self->{loginWorld} = $params->{loginWorld} || 6;
    $self->{loginUsername} = $params->{loginUsername} || "someuser";
    $self->{loginPassword} = $params->{loginPassword} || "secret";
    $self->{loginFormName} = $params->{loginFormName} || "login_form";
    $self->{loginUserField} = $params->{loginUserField} || "username";
    $self->{loginPassField} = $params->{loginPassField} || "password";
    $self->{votingUrl} = $self->{baseUrl} . "/votings.php";
    $self->{marchUrl} = $self->{baseUrl} . "/pohod.php";
    $self->{botCapUrl}  = '';

    $self->{mech} = WWW::Mechanize->new(agent => $self->{userAgent});
    $self->{mech}->cookie_jar(
        HTTP::Cookies->new(
            file           => $self->{cookieFile},
            autosave       => $self->{mechiIgnoreDiscard},
            ignore_discard => 1,
        )
    );

    $self->{mech}->cookie_jar(HTTP::Cookies->new());

    bless ($self, $class);
    return $self;
}

sub login 
{
    my $self = shift;
    my $options = {type => 'normal', username => $self->{loginUsername}, password => $self->{loginPassword}};
    my $utf8_encoded_json = encode_json $options;

    # get login URL
    my $res = $self->{mech}->post($self->{loginUrl}, [
    'options'=> $utf8_encoded_json,
    'action'=>"login",
    'worldId' => $self->{loginWorld},
    'remember' => 0
    ]);    
    my $apiResp = decode_json $res->decoded_content;
    $self->{mech}->get($apiResp->{login_url});

    # get cap url 
    my $capWin = $self->{mech}->content();
    $capWin =~ m/name="form_check" action="(.*)" method="post"/;
    if (!$1) {
    die("Can't get login URL.\nProbably wrong username or password!\n");
    }
    $self->{botCapUrl} = $self->{baseUrl} . '/' . uri_unescape($1);

    # bruteforce cap
    $self->bruteCap();
}

sub bruteCap
{
    my $self = shift;

    my $i = 0;
    foreach (0..100) {
    $i++;
    my $botCapRes = $self->{mech}->post($self->{botCapUrl}, [
        $self->{capPrefix} . '_x' => int(rand($self->{capSizeX} + 1)),
        $self->{capPrefix} . '_y' => int(rand($self->{capSizeY} + 1))
    ]);

    if (!$self->isLogged()) {
        return 0;
    } elsif (!$self->isCaptchaOk()) {
        next;
    } elsif ($self->isLogged() && $self->isCaptchaOk()) {
        return 1;
    }
    }

}

sub goHome
{
    my $self = shift;
    $self->tryAuth();
    $self->{mech}->get($self->{homeUrl});
    my $content = $self->{mech}->content();
    return $content;
}

sub getData
{
    my $self = shift;
    $self->tryAuth();
    $self->{mech}->get($self->{homeUrl});
    my $content = $self->{mech}->content();

    my $data = {};          

    # fetch resources & population
    $content =~ m/<div class="count" id="countGold">(.*)<\/div>/;    
    $data->{resources}->{gold} = $1;
    $data->{resources}->{gold} =~ s/\D//g;   
    $content =~ m/<div class="count" id="countIron">(.*)<\/div>/;    
    $data->{resources}->{iron} = $1;
    $data->{resources}->{iron} =~ s/\D//g;   
    $content =~ m/<div class="count" id="countWood">(.*)<\/div>/;    
    $data->{resources}->{wood} = $1;
    $data->{resources}->{wood} =~ s/\D//g;   
    $content =~ m/<div class="count" id="countFood">(.*)<\/div>/;    
    $data->{resources}->{food} = $1;
    $data->{resources}->{food} =~ s/\D//g;   
    $content =~ m/<div class="count" id="countPop">(.*)<\/div>/;    
    $data->{population} = $1;
    $data->{population} =~ s/\D//g;
    $content =~ m/<div class="storage" id="percPop">(.*)<\/div>/;    
    $data->{populationLimit} = $1;
    $data->{populationLimit} =~ s/\D//g;

    # fetch time resources
    $content =~ m/<span id="PerHour_1">(.*)<\/span>/;
    $data->{resourceStats}->{gold} = $1; 
    $data->{resourceStats}->{gold} =~ s/\D//g;
    $content =~ m/<span id="PerHour_2">(.*)<\/span>/;
    $data->{resourceStats}->{iron} = $1; 
    $data->{resourceStats}->{iron} =~ s/\D//g;
    $content =~ m/<span id="PerHour_3">(.*)<\/span>/;
    $data->{resourceStats}->{wood} = $1; 
    $data->{resourceStats}->{wood} =~ s/\D//g;
    $content =~ m/<span id="PerHour_4">(.*)<\/span>/;
    $data->{resourceStats}->{food} = $1; 
    $data->{resourceStats}->{food} =~ s/\D//g;

    $content =~ m/<p class="myRank">(.*)<\/p>/;
    $data->{myRank} = $1; 
    $data->{myRank} =~ s/\D//g; 

    # money
    $content =~ m/class="coins" title="(.*)">(.*)<\/small>/;
    $data->{money} = $2; 
    $data->{money} =~ s/\D//g;

    # online time
    $content =~ m/<li class="onlineTime" title="(.*)">(.*)<\/li>/;
    $data->{onlineTime} = $2; 
    $data->{onlineTime} =~ s/\D//g;

    # protection
    if ($data->{onlineTime} >= 19) {
        print  "\nWarning! Online time " . $data->{onlineTime} . " hours\n";
        sleep 9 * 60 * 60;
    }

    # online time
    $data->{buildingQueue} = 0; 
    if (my $n = ($content =~ s/<div class="queueBuilding\s?">//g) ) {
        $data->{buildingQueue} = $n; 
    }

    # queueTimeLeft 
    $content =~ m/<div id="buildingsQueue">\s*<span class="totalTime">.*<strong>(.*)<\/strong>\s*<\/span>/;
    $data->{queueTimeLeft} = $self->convetToSceounds($1) if ( defined($1) && length($1) );    

    $data->{buildings} = $self->_parseBuildings();
    $data->{army}->{Units} = $self->_parseUnits();
    $data->{canBuyRes} = $self->_canBuyRes();
    $self->{data} = $data;
    return $data;
}

sub _parseUnits
{
    my $self = shift;
    my $data = {};

    $self->{mech}->get($self->{barracksUrl1});
    my $content = $self->{mech}->content();

    $self->{mech}->get($self->{barracksUrl2});
    $content .= $self->{mech}->content();

    $self->{mech}->get($self->{barracksUrl3});
    $content .= $self->{mech}->content();

    $self->{mech}->get($self->{barracksUrl4});
    $content .= $self->{mech}->content();

    my @matches = $content =~/<li class="(needed.{4})" id="needed.{4}(.{1,3})" title=".*">(.*)<\/li>/g;

    my $i = 0;
    my @groups;
    my $group = [];
    foreach (@matches) {
        $i++;
        push @{$group}, $_;
        if ($i%12 == 0) {
            push @groups, $group;
            $group = [];
        }
    }

    my $unitsNumber = {};

    my @matches1 = $content =~/<span class="unitLevel">(\d{1,6})<\/span>\s*<a href=".*posted_id=(\d{1,3})" title=".*">.*<\/a>/g;

    $i = 0;
    my $prev = 0;
    foreach (reverse @matches1) {
    $i++;
    $unitsNumber->{$_} = 0 if ($i == 1);
    $unitsNumber->{$prev} = $_ if ($i == 2);
    $prev = $_;
    $i = 0 if ($i == 2);

    }

    foreach (@groups) {
        $data->{$_->[1]}->{name} = $self->{unitNames}->{$_->[1]};
        $data->{$_->[1]}->{gold} = $_->[2];
        $data->{$_->[1]}->{iron} = $_->[5];
        $data->{$_->[1]}->{wood} = $_->[8];    
        $data->{$_->[1]}->{food} = $_->[11];
        $data->{$_->[1]}->{count} = $unitsNumber->{$_->[1]};
    }

    return $data;

}

sub sendMarch
{
    my $self = shift;
    my $params = shift;

    $params->{speed} = $params->{speed} || 10;

    $params->{motiv_speed} = $params->{motiv_speed} || 0;    
    $params->{motiv_noble} = $params->{motiv_noble} || 0;    
    $params->{motiv_spy} = $params->{motiv_spy} || 0;    
    $params->{motiv_siege} = $params->{motiv_siege} || 0;    
    $params->{motiv_cargo} = $params->{motiv_cargo} || 0;    
    $params->{motiv_cleanup} = $params->{motiv_cleanup} || 0;

    $params->{army_orders} = $params->{army_orders} || 0;    
    $params->{type} = $params->{type} || 3;    
    $params->{siege_type} = $params->{siege_type} || 0;
    $params->{units} = $params->{units} eq 'all' ?  $self->getAllUnits() : $params->{units};

    $self->tryAuth();

    my $post = [
    'march[0][targetX]' => $params->{targetX},
    'march[0][targetY]' => $params->{targetY},
    'march[0][speed]' => $params->{speed},

    'march[0][motiv_speed]' => $params->{motiv_speed},
    'march[0][motiv_noble]' => $params->{motiv_noble},
    'march[0][motiv_spy]' => $params->{motiv_spy},
    'march[0][motiv_siege]' => $params->{motiv_siege},
    'march[0][motiv_cargo]' => $params->{motiv_cargo},
    'march[0][motiv_cleanup]' => $params->{motiv_cleanup},

    'march[0][army_orders]' => $params->{army_orders},
    'march[0][type]' => $params->{type}, # 3 attack, 4 partol, 5 oborka 
    'march[0][siege_type]' => $params->{siege_type},
    'sendMarch' => 1,

    'add_hero' => $params->{add_hero},
    'march[0][fromX]' => $params->{fromX},    
    'march[0][fromY]' => $params->{fromY},
    'march[0][jsIndex]' => 0,    
    'submitUnits' => 1,        
    ];

    foreach (@{$params->{units}}) {
    if ($_->[1] > 0) {        
        push @{$post}, 'units['. $_->[0] . ']';
        push @{$post}, $_->[1];
    }
    }

    my $res = $self->{mech}->post($self->{marchUrl}, $post); 
}

sub getAllUnits
{
    my $self = shift;
    my $data = $self->getData();

    my $allUnits = [];
    foreach my $key ( keys %{$data->{army}->{Units}} ) {
    push @{$allUnits}, [$key, $data->{army}->{Units}->{$key}->{count}];
    }

    return $allUnits;

}

sub buyPremRes
{
    my $self = shift;

    $self->tryAuth();
    my $res = $self->{mech}->post($self->{premResUrl}, [
    'buy' => "Допълнителен ресурс"
    ]);
}

sub makeUnit
{
    my $self = shift;
    my $id = shift;
    my $value = shift;
    my $speed = shift || 1;

    my $options = [
        'unit[' . $id . ']' => $value,
        'speed[' . $id . ']' => $speed,
    ];
    $self->tryAuth();
    my $res = $self->{mech}->post($self->{barracksUrl} . '?unit_type=' . $self->{unitType}->{$id}, $options);
}

sub makeMaxUnits
{
    my $self = shift;
    my @units = @_;

    my $data = $self->getData();

    foreach (@units) {
        my $id = $_->{id};
        my $value = $_->{value} eq 'max' ? 999999999 : $_->{value};
        my $speed = $_->{speed} || 1;
        my $ratio = $_->{ratio} || 1;

        $_->{maxAvailable} = 0;

        # calc maximum
        my @vals = (
        int($data->{resources}->{gold} / $data->{army}->{Units}->{$id}->{gold}),
        int($data->{resources}->{iron} / $data->{army}->{Units}->{$id}->{iron}),
        int($data->{resources}->{food} / $data->{army}->{Units}->{$id}->{food}),
        int($data->{resources}->{wood} / $data->{army}->{Units}->{$id}->{wood}),
        );

        my @maxs = reverse sort { $b <=> $a } @vals;    

        $_->{maxAvailable} = $value <= $maxs[0] ? $value : int($maxs[0] * $ratio);

        $data->{resources}->{gold} = $data->{resources}->{gold} - $data->{army}->{Units}->{$id}->{gold} * $_->{maxAvailable};
        $data->{resources}->{iron} = $data->{resources}->{iron} - $data->{army}->{Units}->{$id}->{iron} * $_->{maxAvailable};
        $data->{resources}->{food} = $data->{resources}->{food} - $data->{army}->{Units}->{$id}->{food} * $_->{maxAvailable};
        $data->{resources}->{wood} = $data->{resources}->{wood} - $data->{army}->{Units}->{$id}->{wood} * $_->{maxAvailable};

        my $options = [
            'unit[' . $id . ']' => $_->{maxAvailable},
            'speed[' . $id . ']' => $speed,
        ];

        my $res = $self->{mech}->post($self->{barracksUrl} . '?unit_type=' . $self->{unitType}->{$id}, $options);
        sleep 10;
    }

    return @units;
}

sub _canBuyRes
{
    my $self = shift;
    my $data = {};

    $self->{mech}->get($self->{premResUrl});
    my $content = $self->{mech}->content();

    if ($content =~ m/<input\s*type="submit"\s*name="buy"\s*value="(.*)"\s*\/>/) {
        return 1;
    }
    return 0;
}

sub _parseBuildings 
{
    my $self = shift;
    my $data = {};

    $self->{mech}->get($self->{buildUrl});
    my $content = $self->{mech}->content();

    my @matches = $content =~/<div class="buildingItem (.{6,8})Building">\s*<div class="title">\s*<span class="buildingLevel">(.*)<\/span>\s*<a href="info\.php\?posted_id=(.*)\&what=building">(.*)<\/a>\s*<\/div>/g;

    my $i = 0;
    my @groups;
    my $group = [];
    foreach (@matches) {
        $i++;
        push @{$group}, $_;
        if ($i%4 == 0) {
            push @groups, $group;
            $group = [];
        }
    }

    foreach (@groups) {
        $_->[1] = 0 if ($_->[1] eq '');
        $data->{$_->[2]}->{status} = $_->[0];
        $data->{$_->[2]}->{level} = $_->[1];
        $data->{$_->[2]}->{id} = $_->[2];    
        $data->{$_->[2]}->{name} = $self->{buildingIdToName}->{$_->[2]};
    }

    my @matches1 = $content =~/get_building_info\(this\,(\d{1,2})\)" displayed="0"><\/span>\s*<\/div>\s*<ul class="neededResources">\s*<li class=".{10}" title=".*">(\d*).*<\/li>\s*<li class=".{10}" title=".*">(\d*).*<\/li>\s*<li class=".{10}" title=".*">(\d*).*<\/li>\s*<li class=".{10}" title=".*">(\d*).*<\/li>\s*<\/ul>/g;

    my $a = 0;
    my $g = 0;
    foreach (@matches1) {
        $a++;
        if ($a == 1) {
            $data->{$_}->{needGold} = $matches1[$g + 1];
            $data->{$_}->{needIron} = $matches1[$g + 2];
            $data->{$_}->{needWood} = $matches1[$g + 3];
            $data->{$_}->{needFood} = $matches1[$g + 4];
        }

        $g++;
        if ( $a == 5 ) {
            $a = 0;
            next;
        }
    }

    return $data;
}

sub isLogged
{
    my $self = shift;
    my $content = shift || $self->{mech}->content();

    if ( !defined($content) || !length($content)) {
        return 0;        
    }

    if ($content =~/id="login_form"/ || $content =~/Session\sexpired\!\sLogin\sagain/) {
        return 0;
    }

    return 1;
}

sub isCaptchaOk
{
    my $self = shift;
    $self->{mech}->get($self->{capOkUrl});
    my $content = $self->{mech}->content();

    if (!length($content)) {
    return 0;        
    }

    if ($content =~/src="captcha\.php"/ || $content !~/id="mapContainer"/) {
        return 0;
    }

    return 1;
}

sub tryAuth
{
    my $self = shift;
    foreach (1..100) {
        if (!$self->isLogged()) {
            $self->login();
        } elsif (!$self->isCaptchaOk()) {
            $self->bruteCap();
            # Logged BUT CAPTURE not OK
        } else {
            # success !
            return 1;
        }
    }
}

sub canBuild
{
    my $self = shift;
    my $id = shift;

    my $data = $self->getData();    
    if ($data->{buildings}->{$id}->{status} eq 'active' && $data->{buildingQueue} < 1) {
        return 1;
    }    
    return 0;
}

sub makeBuilding
{
    my $self = shift;
    my $id = shift;
    my $options = shift;
    $options->{timeout} = $options->{timeout} ? $options->{timeout} : 1 * 60;

    # try to build
    foreach (1..100) {
        if ($self->canBuild($id)) {            
            # check queue
            my $data = $self->getData();
            my $queue = $data->{buildingQueue};

            my $build = $self->{mech}->get(
                $self->{buildUrl} . "?tb_id=" . $id .
                '&backto=overview.php#center_box'
            );

            sleep 10;            
            my $row_data = $self->getData();
            if ( $row_data->{buildingQueue} > $queue ) {
                return $build;
            } else {
                next;    
            }
        } else {
            my $can_build_time = $self->canBuildTime($id);
            my $time_to_wait = $can_build_time ? $can_build_time : $options->{timeout};
            print "\nsleep for " . $time_to_wait ." secounds\n";
            $self->logout() if ( $time_to_wait > 15 * 60);
            sleep $time_to_wait;
        }
    }
}

sub canBuildTime
{
    my $self = shift;
    my $id = shift;
    my $data = $self->getData();

    my @timeNeeded = (0);

    # get queue timeout
    if (defined($data->{queueTimeLeft}) && length($data->{queueTimeLeft})) {
        push(@timeNeeded, $data->{queueTimeLeft});
    }

    # check Gold
    foreach (('gold','iron','wood','food')) {
        my $uf = ucfirst($_);
        if ($data->{resources}->{$_} < $data->{buildings}->{$id}->{'need'.$uf}) {
            my $money_needed = $data->{buildings}->{$id}->{'need'.$uf} - $data->{resources}->{$_};
            my $money_per_sec = $data->{resourceStats}->{$_} / 60**2;
            my $sec_needed    = ceil ($money_needed / $money_per_sec);
            push (@timeNeeded, $sec_needed);
        }
    }

    @timeNeeded = sort { $b <=> $a } @timeNeeded;    
    print "\ntime needed ".$timeNeeded[0]." sec\n";
    return int($timeNeeded[0]);
}

sub goCamp
{
    my $self = shift;
    my $id = shift;
    my $build = $self->{mech}->get($self->{baseUrl} . '/main.php?campId='.$id.'&location=' . $self->{marchUrl});
    return $self->getData();
}

sub backToMain
{
    my $self = shift;
    my $id = 1957;
    my $build = $self->{mech}->get($self->{baseUrl} . '/main.php?tpid='.$id.'&location=' . $self->{marchUrl});
    return $self->getData();
}

sub convetToSceounds
{
    my $self = shift;
    my $timeStr = shift;

    my $out = 0;

    my @parts = split(':', $timeStr);

    my $i = -1;
    foreach ( reverse @parts ) {
        $i++;
        $out += int($_) * (60**$i); 
    }    

    return $out;
}

sub doVoting
{
    my $self = shift;
    $self->tryAuth();
    foreach (1..12) {
        my $build = $self->{mech}->get($self->{votingUrl} . '?click=' . $_ );
    }
}

sub banProtection
{
    my $self = shift;
    my $data = $self->getData();

    if ($data->{onlineTime} > 18) {
        $self->logout();
        die('Ban protection - ' . $data->{onlineTime} . ' hours');
    }
}

sub logout
{
    my $self = shift;
    my $build = $self->{mech}->get($self->{logoutUrl});
}

1;

Leave a Reply

Your email address will not be published. Required fields are marked *



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>