Решил, что спустя год можно поглядеть, чем френды вконтакте развлекаются. Кроме традиционных тонн политоты, картинок с котятами и прочих непотребств обнаружил ссылку на браузерку Angry Pets. В браузерки никогда не играл, поэтому решил посмотреть, что это за зверь такой. Выяснилось следующее: картинки кавайнейшие (милые котики, пингвинчики и белочки бьют друг другу морды), донат анальнейший (за деньги доход умножают в 8 раз), сюжет, стратегия, безбажность, мануал и прочие привычные удобства отсутствуют.
Игра чуть менее, чем полностью, состоит из следующих нехитрых операций: построить здание, подождать 10 минут, построить юнитов, подождать 10 минут, найти жертву, отправить юниты в атаку, подождать 10 минут, найти жертву, отправить юниты в атаку, подождать 10 минут… (10 минут на высоких уровнях растут экспоненциально. За ускорение — отдельная плата.)
Что делать с такой игрой? Правильно, писать бота. Не играть же в это, честное слово.
[Ссылки могут видеть только зарегистрированные пользователи. ]
Ставим задачу:
Бот должен уметь атаковать заданные списком города по расписанию.
Боту можно задавать, какими юнитами атаковать.
Бот должен не сразу палиться.
Чтобы бот не спалился сразу, во-первых, жертвами для избиения младенцев фарма выбираем игроков, долго не выходивших в онлайн, чтобы они на нас не пожаловались куда следует; во-вторых, имитируем действия пользователя, а не отправляем голые AJAX запросы; в-третьих, задержку между действиями делаем не нулевой, а случайной.
На радость авторам ботов скрипты на сайте не обфусцированы. Единственные упакованные скрипты — это jQuery и иже. И да, есть jQuery, поэтому пользуемся всеми благами цивилизации.
Большинство действий в игре приводят к AJAX запросам. Нажимаешь элемент, уходит запрос, приходит или практически полный экран, или отдельное окошко. Исследуя в отладчике ссылки, видим, что они выглядят как
где метод Main.goToUrl принимает либо строки, либо элементы-ссылки. У большинства игровых ссылок в начале стоит префикс с идентификатором профиля игрока. Сохраняем его.
Как сделать ожидание выполнения AJAX запроса? В jQuery есть славная функция $.ajaxSuccess, в которую можно передать коллбэк, вызываемый после каждого успешного запроса. В него сваливаются событие, объект XMLHttpRequest и аргументы вызова $.ajax. Соответственно, при получении заданного УРЛа вызываем наш коллбэк.
Код:
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();
},
Ну и чисто для единообразия к waitForAjax добавляем waitForAction, когда нужно не ждать AJAX, а просто сделать задержку.
Код:
waitForAction: function (action, success)
{
action();
setTimeout(success, Bot.getClickDelay());
},
Как простой неавтоматизированный смертный часто фармит? Жмёт по кнопке почты, переход в логи, выбирает недавно атакованный город, жмёт «Атаковать», выбирает юниты, жмёт «Набить морду».
[Ссылки могут видеть только зарегистрированные пользователи. ]
Вот и будем повторять эту операцию по кругу. Конечно, в логах нужный город на нужной странице будет не всегда, но писать сложную логику перелистывания без особой мотивации — откровенно лень.
Выбираем жертву и формируем ссылки…
Код:
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;
Переходим по ссылкам и жмём кнопки…
Код:
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 ()
{
Либо выбираем конкретные юниты (доступное количество — в атрибуте инпута max), либо выбираем все (отдельная кнопка), а затем самое важное: нажать «Атаковать»
Код:
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());
})
Попапы-слои при переходе по AJAX-страницам закрываются сами, можно не заморачиваться.
Собственно, всё. Только красивый список атакуемых городов можно прикрутить, чтобы видеть, кого мочим.
В коде массив targetCities заполнить идентификаторами атакуемых городов. При просмотре страницы города идентификатор — это число в самом конце.
В коде объект attackUnits заполнить парами тип юнита — количество юнитов. Идентификаторы типов любезно предоставлены разработчиками (101 из примера — кошачий «боец»).
Задержки настроить в соответствии со своими возможностями: attackInterval — интервал между атаками, clickDelay — задержка при кликах, значения Random — диапазон случайно добавляемой задержки.
Зайти в игру.
Запустить код из отладочной консоли.
Если всё верно, то слева появится список идентификаторов атакуемых городов, а скрипт приступит к атаке на первый город. Текущий город выделяется жирным.
Вообще, меня забавляет наглость хозяев игры. «Абонентская плата» в виде Озверина (без которого играть ещё более уныло) — это 180 руб/мес (WoW, в котором есть всё, чего нет в этой игре, стоит всего лишь в 2 раза дороже). Ресурсов можно покупать на до 120 руб/день (день!), увеличивая скорость добычи ресурсов в 8 раз (соревнование кошельков, неплатящие вообще идут лесом). Просторы для вымогательства доната на ускорялки, детали для ракеты, бонусы для атаки, прохождение квестов (ага, заплатил 300 руб — квест прошёл) и прочую мелочь — вообще безграничны. Обмен ресурсов, вступление в клан и прочее — только за деньги. У них в оплате «выбор игроков» — это монетки на 1500 руб. И это всё прикрывается железным аргументом: «Часть дохода идёт в WWF! Вы спасаете милых пушистых зверьков! Будете возмущаться — забаним нафиг!»