Good day. In this article I will describe the writing of a bot for the online mmo strategy of the game Grepolis. Note that the rules of the game use of such programs is prohibited, for it is banned, and not without reason. I just have a hobby to write bots for games. And writing is not prohibited. To whom the logic and implementation are interesting, I ask under kat.{ 'type' => 'backbone', 'param_id' => 13980, 'subject' => 'Units', 'id' => 4414096, 'param_str' => '{"Units":{"id":13980,"home_town_id":5391,"current_town_id":5391,"sword":23,"slinger":21,"archer":5,"hoplite":10,"rider":0,"chariot":0,"catapult":0,"minotaur":0,"manticore":0,"zyklop":0,"harpy":0,"medusa":0,"centaur":0,"pegasus":0,"cerberus":0,"fury":0,"griffin":0,"calydonian_boar":0,"godsent":34,"big_transporter":0,"bireme":0,"attack_ship":0,"demolition_ship":0,"small_transporter":0,"trireme":0,"colonize_ship":0,"sea_monster":0,"militia":0,"heroes":null,"home_town_link":"<a href=\\"#eyJpZCI6NTM5MSwiaXgiOjUxMSwiaXkiOjYyMywidHAiOiJ0b3duIiwibmFtZSI6IlBlcmwifQ==\\" class=\\"gp_town_link\\">Perl<\\/a>","same_island":true,"current_town_link":"<a href=\\"#eyJpZCI6NTM5MSwiaXgiOjUxMSwiaXkiOjYyMywidHAiOiJ0b3duIiwibmFtZSI6IlBlcmwifQ==\\" class=\\"gp_town_link\\">Perl<\\/a>","current_player_link":"<a href=\\"#eyJuYW1lIjoiUGluZ3ZlaW4iLCJpZCI6e319\\" class=\\"gp_player_link\\">Pingvein<\\/a>"}}', 'time' => 1383837485 } package GrepolisBotModules::Async; use GrepolisBotModules::Log; use IO::Async::Timer::Countdown; use IO::Async::Loop; my $loop = IO::Async::Loop->new; sub delay{ my($delay, $callback) = @_; GrepolisBotModules::Log::echo 1, "Start delay $delay \n"; my $timer = IO::Async::Timer::Countdown->new( delay => $delay, on_expire => $callback, ); $timer->start; $loop->add( $timer ); } sub run{ $loop->later(shift); $loop->run; } 1; #!/usr/bin/perl use strict; use warnings; use Config::IniFiles; use GrepolisBotModules::Request; use GrepolisBotModules::Town; use GrepolisBotModules::Async; use GrepolisBotModules::Log; use utf8; my $cfg = Config::IniFiles->new( -file => "config.ini" ); my $config = { security => { sid => $cfg->val( 'security', 'sid' ), server => $cfg->val( 'security', 'server' ) }, global => { log => $cfg->val( 'global', 'log' ), } }; undef $cfg; my $Towns = []; GrepolisBotModules::Async::run sub{ GrepolisBotModules::Request::init($config->{'security'}); GrepolisBotModules::Log::init($config->{'global'}); GrepolisBotModules::Log::echo(0, "Program started\n"); my $game = GrepolisBotModules::Request::base_request('http://'.$config->{'security'}->{'server'}.'.grepolis.com/game'); $game =~ /"csrfToken":"([^"]+)",/; GrepolisBotModules::Request::setH($1); $game =~ /"townId":(\d+),/; GrepolisBotModules::Log::echo 1, "Town $1 added\n"; push($Towns, new GrepolisBotModules::Town($1)); }; package GrepolisBotModules::Town; use strict; use warnings; use GrepolisBotModules::Request; use GrepolisBotModules::Farm; use GrepolisBotModules::Log; use JSON; my $get_town_data = sub { my( $self ) = @_; my $resp = JSON->new->allow_nonref->decode( GrepolisBotModules::Request::request( 'town_info', 'go_to_town', $self->{'id'}, undef, 0 ) ); $self->{'max_storage'} = $resp->{'json'}->{'max_storage'}; $resp = JSON->new->allow_nonref->decode( GrepolisBotModules::Request::request( 'data', 'get', $self->{'id'}, '{"types":[{"type":"backbone"},{"type":"map","param":{"x":0,"y":0}}]}', 1 ) ); foreach my $arg (@{$resp->{'json'}->{'backbone'}->{'collections'}}) { if( defined $arg->{'model_class_name'} && $arg->{'model_class_name'} eq 'Town' ){ my $town = pop($arg->{'data'}); $self->setResources($town->{'last_iron'}, $town->{'last_stone'}, $town->{'last_wood'}); } } foreach my $data (@{$resp->{'json'}->{'map'}->{'data'}->{'data'}->{'data'}} ) { foreach my $key (keys %{$data->{'towns'}}) { if( defined $data->{'towns'}->{$key}->{'relation_status'} && $data->{'towns'}->{$key}->{'relation_status'} == 1 ){ my $village = new GrepolisBotModules::Farm($data->{'towns'}->{$key}->{'id'}, $self); push($self->{'villages'}, $village); } } } }; my $build_something; $build_something = sub { my $self = shift; GrepolisBotModules::Log::echo 0, "Build request ".$self->{'id'}."\n"; my $response_body = GrepolisBotModules::Request::request('building_main', 'index', $self->{'id'}, '{"town_id":"'.$self->{'id'}.'"}', 0); $response_body =~ m/({.*})/; my %hash = ( JSON->new->allow_nonref->decode( $1 )->{'json'}->{'html'} =~ /BuildingMain.buildBuilding\('([^']+)',\s(\d+)\)/g ); my $to_build = ''; if(defined $hash{'main'} && $hash{'main'}<25){ $to_build = 'main'; }elsif(defined $hash{'academy'}){ $to_build = 'academy'; }elsif(defined $hash{'farm'}){ $to_build = 'farm'; }elsif(defined $hash{'barracks'}){ $to_build = 'barracks'; }elsif(defined $hash{'storage'}){ $to_build = 'storage'; }elsif(defined $hash{'docks'}){ $to_build = 'docks'; }elsif(defined $hash{'stoner'}){ $to_build = 'stoner'; }elsif(defined $hash{'lumber'}){ $to_build = 'lumber'; }elsif(defined $hash{'ironer'}){ $to_build = 'ironer'; } if($to_build ne ''){ my $response_body = GrepolisBotModules::Request::request( 'building_main', 'build', $self->{'id'}, '{"building":"'.$to_build.'","level":5,"wnd_main":{"typeinforefid":0,"type":9},"wnd_index":0,"town_id":"'.$self->{'id'}.'"}', 1 ); } my $time_wait = undef; my $json = JSON->new->allow_nonref->decode($response_body); if(defined $json->{'notifications'}){ foreach my $arg (@{$json->{'notifications'}}) { if( $arg->{'type'} eq 'backbone' && $arg->{'subject'} eq 'BuildingOrder' ){ my $order = JSON->new->allow_nonref->decode($arg->{'param_str'})->{'BuildingOrder'}; $time_wait = $order->{'to_be_completed_at'} - $order->{'created_at'}; } } } if(defined $time_wait){ GrepolisBotModules::Log::echo 0, "Town ".$self->{'id'}." build ".$to_build."\n"; GrepolisBotModules::Async::delay( $time_wait + int(rand(60)), sub {$self->$build_something} ); }else{ GrepolisBotModules::Log::echo 0, "Town ".$self->{'id'}." can not build. Waiting\n"; GrepolisBotModules::Async::delay( 600 + int(rand(300)), sub {$self->$build_something} ); } }; sub setResources{ my $self = shift; my $iron = shift; my $stone = shift; my $wood = shift; $self->{'iron'} = $iron; $self->{'wood'} = $wood; $self->{'stone'} = $stone; GrepolisBotModules::Log::echo 1, "Town ".$self->{'id'}." resources updates iron-".$self->{'iron'}.", stone-".$self->{'stone'}.", wood-".$self->{'wood'}."\n"; } sub needResources{ my $self = shift; my $resources_by_request = shift; if( $self->{'iron'} + $resources_by_request < $self->{'max_storage'} || $self->{'wood'} + $resources_by_request < $self->{'max_storage'} || $self->{'stone'} + $resources_by_request < $self->{'max_storage'} ){ return 1; } return 0; } sub toUpgradeResources{ my $self = shift; return { wood => int($self->{'iron'}/5), stone => int($self->{'wood'}/5), iron => int($self->{'stone'}/5), }; } sub getId{ my $self = shift; return $self->{'id'}; } sub new { my $class = shift; my $self = { id => shift, villages => [], max_storage => undef, iron => undef, wood => undef, stone => undef }; bless $self, $class; GrepolisBotModules::Log::echo 0, "Town ".$self->{'id'}." init started\n"; $self->$get_town_data; GrepolisBotModules::Log::echo 0, "Town ".$self->{'id'}." data gettings finished\n"; $self->$build_something; GrepolisBotModules::Log::echo 0, "Town ".$self->{'id'}." build started\n"; return $self; } 1; package GrepolisBotModules::Farm; use GrepolisBotModules::Request; use GrepolisBotModules::Log; use JSON; my $get_farm_data = sub { my $self = shift; my $resp = JSON->new->allow_nonref->decode( GrepolisBotModules::Request::request( 'farm_town_info', 'claim_info', $self->{'town'}->getId, '{"id":"'.$self->{'id'}.'"}', 0 ) ); $self->{'name'} = $resp->{'json'}->{'json'}->{'farm_town_name'}; $resp->{'json'}->{'html'} =~ /<h4>You\sreceive:\s\d+\sresources<\/h4><ul><li>(\d+)\swood<\/li><li>\d+\srock<\/li><li>\d+\ssilver\scoins<\/li><\/ul>/; $self->{'resources_by_request'} = $1; if($resp->{'json'}->{'html'} =~ /<h4>Upgrade\slevel\s\((\d)\/6\)<\/h4>/ ){ $self->{'level'} = $1; }else{ die('Level not found'); } }; my $upgrade = sub{ my $self = shift; my $donate = $self->{'town'}->toUpgradeResources(); $json = '{"target_id":'.$self->{'id'}.',"wood":'.$donate->{'wood'}.',"stone":'.$donate->{'stone'}.',"iron":'.$donate->{'iron'}.',"town_id":"'.$self->{'town'}->getId().'"}'; my $response_body = GrepolisBotModules::Request::request('farm_town_info', 'send_resources', $self->{'town'}->getId(), $json, 1); GrepolisBotModules::Log::echo 1, "Village send request. Town ID ".$self->{'town'}->getId()." Village ID ".$self->{'id'}."\n"; $self->$get_farm_data; }; my $claim = sub{ my $self = shift; $json = '{"target_id":"'.$self->{'id'}.'","claim_type":"normal","time":300,"town_id":"'.$self->{'town'}->getId.'"}'; my $response_body = GrepolisBotModules::Request::request('farm_town_info', 'claim_load', $self->{'town'}->getId, $json, 1); my $json = JSON->new->allow_nonref->decode($response_body)->{'json'}; if(defined $json->{'notifications'}){ foreach my $arg (@{$json->{'notifications'}}) { if( $arg->{'type'} eq 'backbone' && $arg->{'subject'} eq 'Town' ){ my $town = JSON->new->allow_nonref->decode($arg->{'param_str'})->{'Town'}; $self->{'town'}->setResources($town->{'last_iron'}, $town->{'last_stone'}, $town->{'last_wood'}); } } } GrepolisBotModules::Log::echo 1, "Farm ".$self->{'id'}." claim finished\n"; }; my $needUpgrade = sub { my $self = shift; if($self->{'level'} < 6){ return true; }else{ return false; } }; my $tick; $tick = sub { my $self = shift; if($self->{'town'}->needResources($self->{'resources_by_request'})){ $self->$claim(); GrepolisBotModules::Async::delay( 360 + int(rand(240)), sub { $self->$tick} ); }elsif($self->$needUpgrade()){ $self->$upgrade(); GrepolisBotModules::Async::delay( 600 + int(rand(240)), sub { $self->$tick} ); } }; sub new { my $class = shift; my $self = { id => shift, name => undef, resources_by_request => undef, town => shift, level => undef }; GrepolisBotModules::Log::echo 0, "Farm ".$self->{'id'}." init started\n"; bless $self, $class; $self->$get_farm_data; GrepolisBotModules::Log::echo 0, "Farm ".$self->{'id'}." data gettings finished\n"; $self->$tick; GrepolisBotModules::Log::echo 0, "Farm ".$self->{'id'}." ticker started\n"; return $self; } 1; if($response_body =~ /^{/){ my $json = JSON->new->allow_nonref->decode( $response_body )->{'json'}; if(defined $json->{'notifications'}){ foreach my $arg (@{$json->{'notifications'}}) { if( ( $arg->{'type'} ne 'building_finished' && $arg->{'type'} ne 'newreport' && ( $arg->{'type'} ne 'backbone' || $arg->{'type'} eq 'backbone' && ( !(defined $arg->{'subject'}) || ( $arg->{'subject'} ne 'BuildingOrder' && $arg->{'subject'} ne 'Town' && $arg->{'subject'} ne 'PlayerRanking' && $arg->{'subject'} ne 'Buildings' && $arg->{'subject'} ne 'IslandQuest' && $arg->{'subject'} ne 'TutorialQuest' ) ) ) ) ){ GrepolisBotModules::Log::dump 5, $arg; } } } } Source: https://habr.com/ru/post/201272/
All Articles