📜 ⬆️ ⬇️

Bot for browser Angry Pets

Warning : the bot is simple to disgrace. You are unlikely to learn something new. Article just for fun.

I decided that after a year you can look at what friends vkontakte fun. In addition to traditional tons of political politics, pictures of kittens and other indecencies, I found a link to the Angry Pets browser. I never played browser, so I decided to see what kind of animal it is. It turned out the following: the pictures are the kuaiest (cute seals, penguins and little squirrels hit each other’s muzzles), the donate is more anonymous (they multiply the income 8 times for money), the plot, the strategy, the hopelessness, the manual and other usual amenities are absent.

The game slightly less than completely consists of the following simple operations: build a building, wait 10 minutes, build units, wait 10 minutes, find a victim, send units to attack, wait 10 minutes, find a victim, send units to attack, wait 10 minutes ... (10 minutes at high levels grow exponentially. For acceleration - a separate fee.)
')
What to do with this game? That's right, write a bot. Do not play it , honestly.



We set the task:


To prevent the bot from sleeping right away, firstly, we choose the players who did not go online for a long time as victims for beating farm babies so that they would not complain about where they should go; secondly, we imitate actions of the user, but we do not send naked AJAX requests; thirdly, the delay between actions is not zero, but random.

To the joy of the authors of the bots, the scripts on the site are not obfuscated. The only packaged scripts are jQuery and the like. And yes, there is jQuery, so we use all the benefits of civilization.

Most of the actions in the game lead to AJAX requests. You click an item, the request leaves, either the almost full screen comes up, or a separate window. Examining the links in the debugger, we see that they look like
<a href="/10193192/city/view/10009466" data-link-handled="1" onclick="Main.goToUrl(this);return false;"></a> 
where the method Main.goToUrl accepts either strings or link elements. Most game links have a prefix with a player profile id at the beginning. Save it.

  profilePath: window.location.pathname.match(/^\/\d+\//)[0], 

How to wait for an AJAX request? JQuery has a nice $ .ajaxSuccess function that you can pass a callback to be called after each successful request. Event, object XMLHttpRequest and arguments of a call of $ .ajax fall down into it. Accordingly, when we receive a given URL, we call our callback.

  ajaxCallbacks: {}, run: function () { ... $('html').ajaxSuccess(Bot.ajaxSuccess); ... }, ajaxSuccess: function (e, xhr, settings) { var ajaxUrl = null, ajaxCallback = null; $.each(Bot.ajaxCallbacks, function (url, callback) { var fullUrl = Bot.profilePath + url; if (settings.url.substr(0, fullUrl.length) == fullUrl) { ajaxUrl = url; ajaxCallback = callback; } }); if (ajaxCallback) { Bot.ajaxCallbacks[ajaxUrl] = null; setTimeout(ajaxCallback, Bot.getClickDelay()); } }, waitForAjax: function (pageUrl, gotoPage, success) { Bot.ajaxCallbacks[pageUrl] = success; gotoPage(); }, 

Well, purely for uniformity, we add waitForAction to waitForAjax, when you need not to wait for AJAX, but just to make a delay.

  waitForAction: function (action, success) { action(); setTimeout(success, Bot.getClickDelay()); }, 

How does a simple humanized mortal often farm? Clicks on the mail button, go to the logs, selects the recently attacked city, presses "Attack", selects units, presses "Fill the face."



So we will repeat this operation in a circle. Of course, in the logs the desired city will not always be on the necessary page, but it is frankly too lazy to write the complex logic of turning over without much motivation.

Choosing a victim and forming links ...
  attackNext: function () { if (Bot.targetCities.length == 0) return; if (!Bot.targetCities[Bot.currentTargetCity]) Bot.currentTargetCity = 0; var targetCity = Bot.targetCities[Bot.currentTargetCity++], targetCityUrl = 'city/view/' + targetCity, attackCityUrl = 'attack/' + targetCity; 

Follow the links and click the buttons ...
  Bot.waitForAjax('pm/inbox', function () { Main.goToUrl(Bot.profilePath + 'pm/inbox'); }, function () { Bot.waitForAjax('pm/logs', function () { Main.goToUrl(Bot.profilePath + 'pm/logs'); }, function () { Bot.waitForAjax(targetCityUrl, function () { Main.goToUrl(Bot.profilePath + targetCityUrl); }, function () { Bot.waitForAjax(attackCityUrl, function () { $('button[onclick^="Attack.showAttackAlert"]').click(); }, function () { 

Either select specific units (the available amount is in the attribute max), or select all (a separate button), and then the most important thing: click "Attack".
  Bot.waitForAction(function () { var count = 0; $.each(Bot.attackUnits, function (unitType, unitNum) { var ctl = $('input[name="units[' + unitType + ']"]'); ctl.val(Math.min(ctl.attr('max'), unitNum)).change(); count++; }); if (count == 0) { $('span[onclick^="Attack.ChooseEveryone"]').click(); } }, function () { $('button[type=submit]').click(); setTimeout(Bot.attackNext, Bot.getAttackInterval()); }) 

Popup layers, when navigating through AJAX-pages, close themselves, you can not bother.

Actually, everything. Only a beautiful list of attacked cities can be screwed to see who we dab.

Full code:
 window.Bot = { attackInterval: /*5.5*/8 * 60 * 1000, // 8 min attackIntervalRandom: 1.2 * 60 * 1000, // 1.2 min clickDelay: 3 * 1000, // 3 sec clickDelayRandom: 4 * 1000, // 4 sec targetCities: [ //12345678 ], attackUnits: { //101: 99 }, profilePath: window.location.pathname.match(/^\/\d+\//)[0], currentTargetCity: 0, ajaxCallbacks: {}, run: function () { var box = '<div style="position: absolute; background: #fff; padding: 10px; border-radius: 10px; left: 20px; top: 20px; z-index: 666666">'; $.each(Bot.targetCities, function (_, cityId) { box += '<a class="bot-target-city" data-link-handled="1" onclick="Main.goToUrl(this);return false;"' + ' id="bot-target-city-' + cityId + '"' + ' href="' + Bot.profilePath + 'city/view/' + cityId + '">' + cityId + '</a><br>'; }); box += '</div>'; $('body').append(box); $('html').ajaxSuccess(Bot.ajaxSuccess); Bot.attackNext(); }, ajaxSuccess: function (e, xhr, settings) { var ajaxUrl = null, ajaxCallback = null; $.each(Bot.ajaxCallbacks, function (url, callback) { var fullUrl = Bot.profilePath + url; if (settings.url.substr(0, fullUrl.length) == fullUrl) { ajaxUrl = url; ajaxCallback = callback; } }); if (ajaxCallback) { Bot.ajaxCallbacks[ajaxUrl] = null; setTimeout(ajaxCallback, Bot.getClickDelay()); } else { console.log('Not recognized ' + settings.url); } }, waitForAjax: function (pageUrl, gotoPage, success) { Bot.ajaxCallbacks[pageUrl] = success; gotoPage(); }, waitForAction: function (action, success) { action(); setTimeout(success, Bot.getClickDelay()); }, getAttackInterval: function () { return parseInt(Bot.attackInterval + Math.random() * Bot.attackIntervalRandom); }, getClickDelay: function () { return parseInt(Bot.clickDelay + Math.random() * Bot.clickDelayRandom); }, attackNext: function () { if (Bot.targetCities.length == 0) return; if (!Bot.targetCities[Bot.currentTargetCity]) Bot.currentTargetCity = 0; var targetCity = Bot.targetCities[Bot.currentTargetCity++], targetCityUrl = 'city/view/' + targetCity, attackCityUrl = 'attack/' + targetCity; $('a.bot-target-city').css({ fontWeight: 'normal' }); $('a#bot-target-city-' + targetCity).css({ fontWeight: 'bold' }); Bot.waitForAjax('pm/inbox', function () { Main.goToUrl(Bot.profilePath + 'pm/inbox'); }, function () { Bot.waitForAjax('pm/logs', function () { Main.goToUrl(Bot.profilePath + 'pm/logs'); }, function () { Bot.waitForAjax(targetCityUrl, function () { Main.goToUrl(Bot.profilePath + targetCityUrl); }, function () { Bot.waitForAjax(attackCityUrl, function () { $('button[onclick^="Attack.showAttackAlert"]').click(); }, function () { Bot.waitForAction(function () { var count = 0; $.each(Bot.attackUnits, function (unitType, unitNum) { var ctl = $('input[name="units[' + unitType + ']"]'); ctl.val(Math.min(ctl.attr('max'), unitNum)).change(); count++; }); if (count == 0) { $('span[onclick^="Attack.ChooseEveryone"]').click(); } }, function () { $('button[type=submit]').click(); setTimeout(Bot.attackNext, Bot.getAttackInterval()); }) }); }); }); }); } }; Bot.run(); 


Instructions for use:


In general, I am amused by the arrogance of the game owners. "Subscription fee" in the form of Ozverin (without which to play even more sadly) is 180 rubles / month (WoW, which has everything that is not in this game, costs only 2 times more expensive). Resources can be bought for up to 120 rubles / day (day!), Increasing the rate of resource extraction by 8 times (competition of wallets, non-paying in general go to the forest). The open spaces for extorting Donat on accelerators, details for a rocket, bonuses for attacking, completing quests (yeah, paid 300 rubles - the quest was completed) and other trifles are generally limitless. Exchange of resources, joining the clan, etc. - just for the money. They have to pay "the choice of players" - this is a coin for 1500 rubles. And this is all covered by an iron argument: “Part of the income goes to WWF! You are saving cute furry animals! You will be outraged - ban nafig! "

And because people pay. I go nuts.

Source: https://habr.com/ru/post/148323/


All Articles