Простой бот для приложений Вконтакте на Python
Думаю все знают про приложения-игралки, со всяким рейтингом среди друзей, популярные среди обитателей вконтакта. В этом топике я расскажу, как можно быть в них на первом месте, при этом не задрачиваясь, и не отупляя свой мозг.
В большинстве случаев, приложения обмениваются с сервером информацией по HTTP, посылая информацию POST-запросом, в ответ получая XML. Наша задача состоит в том, чтобы эмитировать эти запросы. Нашим подопытным кроликом будет приложение «Тюгяга»
Какие инструменты нам потребуются:
- Python — Любимый всеми язык. Будем использовать только стандартную библиотеку.
- Charles — простой HTTP-прокси. Его мы будем использовать для перехвата и анализа запросов.
Итак, приступим:
Для начала запустим Charles. Разобраться в нём сможет даже ребёнок. Приложение советую запускать в Firefox’е. Попробуйте выполнить какое-нибудь действие в «Тюряге», например собрать сигареты, и посмотрите на последний запрос. В общем случае вы увидите что-то вроде этого:
POST /prison/universal.php?office HTTP/1.1
Host: 109.234.156.250
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Referer: xxxxx
Content-type: application/x-www-form-urlencoded
Content-length: 111
method=office&sig=4b8874255d3f868e4e9f4006e51635de&user=xxxxxxxxx&getidea=1&key=xxxxxxxxxxxxxx
Тут мы имеем URI, HTTP-заголовки, пустую строку, и данные, отправленные на сервер. Что же отправляется на сервер:
key
— Ключ, необходимый для идентификации вас именно в этом приложении. Предоставляется контактом, для каждого приложения разный. По этому ключу невозможно взломать вашу страницу или узнать какие-нибудь ваши данные. Периодически меняется, кстати.
user
— Ваш id.
sig
— Сигнатура, генерируемая тюрягой. Честно говоря, я не разобрался для чего она служит и по какому принципу генерируется. Но без неё никак. Лично я просто отловил их несколько штук и рандомно подставляю для каждого запроса. :)
method
— Сообщает серверу, что за действие вы совершили.
Отправляются ещё и дополнительные данные, такие как getidea — сообщает сколько сигарет вы собрали.
Переходим к питону:
#!/usr/bin/env python3 # t.py import urllib, time, http.client, random, threading from xml.dom import minidom
Импортируем необходимые модули из стандартной библиотеки
xml.dom.minidom нужен если вы собирайтесь обрабатывать XML-ответ.
sigs = ('21ddcf3ea352f24ebdfe65bce51258da','4b8874255d3f868e4e9f4006e51635de','8dbedd3e931c8fd4fc97fa0fa74a1a20', '2243effc5a45a32c67b06898fb0b9548','7f47c0b6e9e385b8234ca9dce375a0c5','bdab387b62bed654492b8c93670a537e', 'f17687ea1a535d61124dadb30887d14c','938b2f649aec28319ae2433ead41dae5') h ={ 'Host':'188.93.20.139', 'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language':'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Charset':'windows-1251,utf-8;q=0.7,*;q=0.7', 'Keep-Alive':'115', 'Content-type':'application/x-www-form-urlencoded', } u = 'Ваш_ID' k = 'Ваш key, для идентификации в приложении'
Создаём кортеж из сигнатур, заголовки в форме словаря(лишние я удалил), и две переменные.
def request(post): global h # используем в функции глобальную переменную h connection = http.client.HTTPConnection('109.234.155.196:80') # Создаём объект соединения connection.request('POST', '/prison/universal.php', body = post, headers = h) # отправляем запрос resp = connection.getresponse() # получаем ответ return resp.read() # возвращаем ответ из функции
Т.к. отправлять данные на придётся часто, создадим для этого отдельную функцию
def take_sigareties(): global sigs, u, k # используем в функции глобальные переменные post = urllib.parse.urlencode({ 'method' : 'office', 'getidea' : '5', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) # с помощью метода urllib.parse.urlencode подготавливаем данные для отправки. данные скармливаем методу в виде словаря while True: # запускаем бесконечный цикл for _ in range(0,20): # запускае цикл for на 20 раз request(post) # с помощью функции, которую мы создали ранее, отправляем данные на сервер print('Собрал сиги') time.sleep(5*20*60+15) # усыпляем поток выполнения на 6015 секунд. т.е. следующая итерация for произойдёт через указанное время. time.sleep(8*60*60) # после того как собрали 20 раз сиги, усыпляем поток на 8 часиков, чтобы нас не спалили ;)
Создаём функцию для сбора сигареток
По аналогии создадим функцию для сбора прибыли раз в 8 часов:
def take_rewards(): global sigs, u, k post = urllib.parse.urlencode({ 'method' : 'getAllBuildingsRewards', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) while True: request(post) print('Собрал прибыль') time.sleep(8*60*60+15)
Т.к. нам надо чтобы функции работали независимо друг от друга, запустим их в отдельных потоках:
t1 = threading.Thread(target = take_sigareties) # создаём объект потока, target'у подаётся созданная функция t1.deamon = True # это нужно для того, чтобы при закрытии основного потока, дочерний поток тоже закрывался t2 = threading.Thread(target = take_rewards) t2.deamon = True t1.start() # запускаем поток на выполнение t2.start()
На этом собственно всё, запускаем скрипт, и радуемся, что больше не надо заходить в приложение каждые 20 минут, чтобы собрать сигоретки.
По аналогии можно писать ботов для других приложений.
Вот полный код бота.
#!/usr/bin/env python3 # t.py import urllib, time, http.client, random, threading from xml.dom import minidom sigs = ('21ddcf3ea352f24ebdfe65bce51258da','4b8874255d3f868e4e9f4006e51635de','8dbedd3e931c8fd4fc97fa0fa74a1a20', '2243effc5a45a32c67b06898fb0b9548','7f47c0b6e9e385b8234ca9dce375a0c5','bdab387b62bed654492b8c93670a537e', 'f17687ea1a535d61124dadb30887d14c','938b2f649aec28319ae2433ead41dae5') h ={ 'Host':'188.93.20.139', 'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language':'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Charset':'windows-1251,utf-8;q=0.7,*;q=0.7', 'Keep-Alive':'115', 'Content-type':'application/x-www-form-urlencoded', } u = 'Ваш_ID' k = 'Ваш key' def request(post): global h # используем в функции глобальную переменную h connection = http.client.HTTPConnection('109.234.155.196:80') # Создаём объект соединения connection.request('POST', '/prison/universal.php', body = post, headers = h) # отправляем запрос resp = connection.getresponse() # получаем ответ return resp.read() # возвращаем ответ из функции def take_rewards(): global sigs, u, k post = urllib.parse.urlencode({ 'method' : 'getAllBuildingsRewards', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) while True: request(post) print('Собрал прибыль') time.sleep(8*60*60+15) def take_sigareties(): global sigs, u, k # используем в функции глобальные переменные post = urllib.parse.urlencode({ 'method' : 'office', 'getidea' : '5', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) # с помощью метода urllib.parse.urlencode подготавливаем данные для отправки. данные скармливаем методу в виде словаря while True: # запускаем бесконечный цикл for _ in range(0,20): # запускае цикл for на 20 раз request(post) # с помощью функции, которую мы создали ранее, отправляем данные на сервер print('Собрал сиги') time.sleep(5*20*60+15) # усыпляем поток выполнения на 6015 секунд. т.е. следующая итерация for произойдёт через указанное время. time.sleep(8*60*60) # после того как собрали 20 раз сиги, усыпляем поток на 8 часиков, чтобы нас не спалили ;) def do_city_action(): global sigs, u, k while True: action_id = 1 while action_id < 8: for _ in range(0,5): post = urllib.parse.urlencode({ 'method' : 'doCityAction', 'city' : '1', 'action_id' : str(action_id), 'action_type' : '3', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) response = request(post) print('Выполнил задание') if int(minidom.parseString(str(response, 'utf8')).getElementsByTagName('energy')[0].firstChild.data) < 10: time.sleep(40*5*60) action_id +=1 def vote_friends_to_gym(): global sigs, u, k friends = () # кортеж друзей, которых вы хотите позвать в спортзал while True: for friend in friends: post = urllib.parse.urlencode({ 'method' : 'voteForFriend', 'model_id' : '1', 'sex' : '0', 'friend_uid' : friend, 'vote' : '5', 'username' : 'London Eyes', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) request(post) print('Позвал ' + friend + ' в спортзал') time.sleep(24*60*60+15) def break_terpilas_faces(): global sigs, u, k terpilas = () # кортеж друзей, на которых вы хотите наехать while True: for terpila in terpilas: post = urllib.parse.urlencode({ 'method' : 'challengeToDuel', 'enemy' : terpilas, 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) request(post) print('Наехал на ' + terpila) time.sleep(24*60*60+15) def send_presents(): recipients = '' # чезез запятую id друзей, которым вы хотите заслать подогрев while True: post = urllib.parse.urlencode({ 'method' : 'sendPresent', 'recipients' : recipients, 'present_id' : '5', 'sig' : random.choice(sigs), 'user' : u, 'key' : k }) request(post) print('Послал подогревы') time.sleep(24*60*60+15) t1 = threading.Thread(target = take_rewards) # создаём объект потока, target'у подаётся созданная функция t1.deamon = True # это нужно для того, чтобы при закрытии основного потока, дочерний поток тоже закрывался t2 = threading.Thread(target = take_sigareties) t2.deamon = True t3 = threading.Thread(target = do_city_action) t3.deamon = True t4 = threading.Thread(target = vote_friends_to_gym) t4.deamon = True t5 = threading.Thread(target = break_terpilas_faces) t5.deamon = True t6 = threading.Thread(target = send_presents) t6.deamon = True t1.start() # запускаем поток на выполнение t2.start() t3.start() t4.start() t5.start() t6.start()