From 3cb2b5dc34144358237793c7faabf9a4bb61c927 Mon Sep 17 00:00:00 2001 From: python273 Date: Thu, 26 Sep 2013 20:49:54 +0400 Subject: [PATCH 1/9] 4.6.1 Delete windows line separator --- .gitignore | 1 + MANIFEST | 3 +- example.py | 64 +++--- jconfig/__init__.py | 38 ++-- jconfig/jconfig.py | 84 ++++---- vk_api/vk_api.py | 470 ++++++++++++++++++++++---------------------- vk_api/vk_upload.py | 202 +++++++++---------- 7 files changed, 436 insertions(+), 426 deletions(-) diff --git a/.gitignore b/.gitignore index a059d759..20423f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ upload_to_PyPi.py .project .pydevproject .settings/ +.idea/ \ No newline at end of file diff --git a/MANIFEST b/MANIFEST index 71e9f48e..eed4a61b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -5,5 +5,4 @@ jconfig\jconfig.py test\test.py vk_api\__init__.py vk_api\vk_api.py -vk_api\upload\__init__.py -vk_api\upload\vk_upload.py +vk_api\vk_upload.py diff --git a/example.py b/example.py index c7cc5ef6..90306877 100644 --- a/example.py +++ b/example.py @@ -1,32 +1,32 @@ -''' -@author: Kirill Python -@contact: http://vk.com/python273 -@license Apache License, Version 2.0, see LICENSE file - -Copyright (C) 2013 -''' - -# -*- coding: utf-8 -*- -import vk_api - - -def main(): - u""" Пример получения последнего сообщения со стены """ - - login = u'python@vk.com' - password = u'mypassword' - - try: - vk = vk_api.VkApi(login, password) # Авторизируемся - except vk_api.authorization_error as error_msg: - print(error_msg) # В случае ошибки выведем сообщение - return # и выйдем - - values = { - 'count': 1 # Получаем только одно сообщение - } - response = vk.method('wall.get', values) # Используем метод wall.get - print(response[1]['text']) # Печатаем текст последнего поста со стены - -if __name__ == '__main__': - main() +''' +@author: Kirill Python +@contact: http://vk.com/python273 +@license Apache License, Version 2.0, see LICENSE file + +Copyright (C) 2013 +''' + +# -*- coding: utf-8 -*- +import vk_api + + +def main(): + u""" Пример получения последнего сообщения со стены """ + + login = u'python@vk.com' + password = u'mypassword' + + try: + vk = vk_api.VkApi(login, password) # Авторизируемся + except vk_api.authorization_error as error_msg: + print(error_msg) # В случае ошибки выведем сообщение + return # и выйдем + + values = { + 'count': 1 # Получаем только одно сообщение + } + response = vk.method('wall.get', values) # Используем метод wall.get + print(response[1]['text']) # Печатаем текст последнего поста со стены + +if __name__ == '__main__': + main() diff --git a/jconfig/__init__.py b/jconfig/__init__.py index e68b8eb1..e8190ebe 100644 --- a/jconfig/__init__.py +++ b/jconfig/__init__.py @@ -1,19 +1,19 @@ -''' -@author: Kirill Python -@contact: http://vk.com/python273 -@license Apache License, Version 2.0, see LICENSE file - -Copyright (C) 2013 -''' - -__author__ = "Kirill Python" -__version__ = "1.1" -__email__ = "mikeking568@gmail.com" -__contact__ = "https://vk.com/python273" - - -import sys -if sys.version_info[0] == 2: - from jconfig import * -else: - from .jconfig import * +''' +@author: Kirill Python +@contact: http://vk.com/python273 +@license Apache License, Version 2.0, see LICENSE file + +Copyright (C) 2013 +''' + +__author__ = "Kirill Python" +__version__ = "1.1" +__email__ = "mikeking568@gmail.com" +__contact__ = "https://vk.com/python273" + + +import sys +if sys.version_info[0] == 2: + from jconfig import * +else: + from .jconfig import * diff --git a/jconfig/jconfig.py b/jconfig/jconfig.py index 60d957f9..bdf28bac 100644 --- a/jconfig/jconfig.py +++ b/jconfig/jconfig.py @@ -1,42 +1,42 @@ -''' -@author: Kirill Python -@contact: http://vk.com/python273 -@license Apache License, Version 2.0, see LICENSE file - -Copyright (C) 2013 -''' - -# -*- coding: utf-8 -*- -import os -import json - - -class config: - def __init__(self, section, filename='config'): - self.section = section # Секция настроек - self.filename = filename # Файл с настройками - self.all = self.parse() # Все настройки - self.settings = self.all.get(section, {}) # Настройки секции - - def __getitem__(self, item): - return self.settings.get(item, {}) - - def __setitem__(self, key, value): - self.settings.update({key: value}) - self.all.update({self.section: self.settings}) - self.update(self.all) - - def parse(self): - fileis = os.path.exists(self.filename) - if fileis: - settings = json.load(open(self.filename, 'r')) - return settings - else: - self.update() - return {} - - def update(self, settings=None): - if not settings: - settings = {} - - json.dump(settings, open(self.filename, 'w')) +''' +@author: Kirill Python +@contact: http://vk.com/python273 +@license Apache License, Version 2.0, see LICENSE file + +Copyright (C) 2013 +''' + +# -*- coding: utf-8 -*- +import os +import json + + +class config(object): + def __init__(self, section, filename='config'): + self.section = section # Секция настроек + self.filename = filename # Файл с настройками + self.all = self.parse() # Все настройки + self.settings = self.all.get(section, {}) # Настройки секции + + def __getitem__(self, item): + return self.settings.get(item, {}) + + def __setitem__(self, key, value): + self.settings.update({key: value}) + self.all.update({self.section: self.settings}) + self.update(self.all) + + def parse(self): + fileis = os.path.exists(self.filename) + if fileis: + settings = json.load(open(self.filename, 'r')) + return settings + else: + self.update() + return {} + + def update(self, settings=None): + if not settings: + settings = {} + + json.dump(settings, open(self.filename, 'w')) diff --git a/vk_api/vk_api.py b/vk_api/vk_api.py index bb209496..b213509d 100644 --- a/vk_api/vk_api.py +++ b/vk_api/vk_api.py @@ -1,230 +1,240 @@ -''' -@author: Kirill Python -@contact: http://vk.com/python273 -@license Apache License, Version 2.0, see LICENSE file - -Copyright (C) 2013 -''' - -# -*- coding: utf-8 -*- -import jconfig -import re -import requests -import time - - -class VkApi(): - def __init__(self, - login=None, password=None, number=None, - sid=None, token=None, - proxies=None, - version='5.0', app_id=2895443, scope=2097151): - ''' - :param login: Логин ВКонтакте - :param password: Пароль ВКонтакте - :param number: Номер при проверке безопасности - Номер: +7 12345678 90 - number = 12345678 - :param sid: cookie remixsid - :param token: access_token - :param proxies: proxy server - {'http': 'http://127.0.0.1:8888/', - 'https' : 'https://127.0.0.1:8888/'} - :param version: Версия API (default: '5.0') - :param app_id: Standalone-приложение (default: 2895443) - :param scope: Запрашиваемые права (default: 2097151) - ''' - - self.login = login - self.password = password - self.number = number - - self.sid = sid - self.token = {'access_token': token} - - self.version = version - self.app_id = app_id - self.scope = scope - - self.settings = jconfig.config(login) - - self.http = requests.Session() - self.http.proxies = proxies # Ставим прокси - self.http.headers = { # Притворимся браузером - 'User-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) ' \ - 'Gecko/20100101 Firefox/20.0' - } - self.http.verify = False - - if login and password: - self.sid = self.settings['remixsid'] - self.token = self.settings['access_token'] - - if not self.check_sid(): - self.vk_login() - - if not self.check_token(): - self.api_login() - - def vk_login(self, captcha_sid=None, captcha_key=None): - ''' Авторизцаия ВКонтакте с получением cookies remixsid ''' - - url = 'https://login.vk.com/' - values = { - 'act': 'login', - 'utf8': '1', - 'email': self.login, - 'pass': self.password - } - - if captcha_sid and captcha_key: - values.update({ - 'captcha_sid': captcha_sid, - 'captcha_key': captcha_key - }) - - self.http.cookies.clear() - response = self.http.post(url, values) - - if 'remixsid' in self.http.cookies: - remixsid = self.http.cookies['remixsid'] - self.settings['remixsid'] = remixsid - - # Нужно для авторизации в API - self.settings['forapilogin'] = { - 'p': self.http.cookies['p'], - 'l': self.http.cookies['l'] - } - - self.sid = remixsid - - elif 'sid=' in response.url: - raise authorization_error('Authorization error (capcha)') - else: - raise authorization_error('Authorization error (bad password)') - - if 'security_check' in response.url: - if self.number: - number_hash = regexp(r'security_check.*?hash: \'(.*?)\'\};', - response.text)[0] - - values = { - 'act': 'security_check', - 'al': '1', - 'al_page': '3', - 'code': self.number, - 'hash': number_hash, - 'to': '' - } - - response = self.http.post('https://vk.com/login.php', values) - - if response.text.split('')[4] == '4': - return - - raise authorization_error('Authorization error (enter number)') - - def check_sid(self): - ''' Прверка Cookies remixsid на валидность ''' - - if self.sid: - url = 'https://vk.com/feed2.php' - self.http.cookies.update({ - 'remixsid': self.sid, - 'remixlang': '0', - 'remixsslsid': '1' - }) - - response = self.http.get(url).json() - - if response['user']['id'] != -1: - return response - - def api_login(self): - ''' Получение токена через Desktop приложение ''' - - url = 'https://oauth.vk.com/authorize' - values = { - 'client_id': self.app_id, - 'scope': self.scope, - 'response_type': 'token', - } - - self.http.cookies.update(self.settings['forapilogin']) - self.http.cookies.update({'remixsid': self.sid}) - - response = self.http.post(url, values) - - if not 'access_token' in response.url: - url = regexp(r'location\.href = "(.*?)"\+addr;', response.text)[0] - response = self.http.get(url) - - if 'access_token' in response.url: - params = response.url.split('#')[1].split('&') - - token = {} - for i in params: - x = i.split('=') - token.update({x[0]: x[1]}) - - self.settings['access_token'] = token - self.token = token - else: - raise authorization_error('Authorization error (api)') - - def check_token(self): - ''' Прверка access_token на валидность ''' - - if self.token.get('access_token'): - try: - self.method('isAppUser') - except api_error: - return False - - return True - - def method(self, method, values=None): - ''' Использование методов API - - method - название метода - 'users.get' - - values - параметры - {'uids': 1} - ''' - - url = 'https://api.vk.com/method/%s' % method - - if not values: - values = {} - - values.update({'v': self.version}) - - if self.token: - values.update({'access_token': self.token['access_token']}) - - response = self.http.post(url, values).json() - if 'error' in response: - raise api_error(response['error']) - else: - return response['response'] - - -def regexp(reg, string): - ''' Поиск по регулярке ''' - - reg = re.compile(reg, re.IGNORECASE | re.DOTALL) - reg = reg.findall(string) - return reg - - -class vk_api_error(Exception): - pass - - -class authorization_error(vk_api_error): - pass - - -class api_error(vk_api_error): - pass +''' +@author: Kirill Python +@contact: http://vk.com/python273 +@license Apache License, Version 2.0, see LICENSE file + +Copyright (C) 2013 +''' + +# -*- coding: utf-8 -*- +import jconfig +import re +import requests +# import time + + +class VkApi(object): + def __init__(self, + login=None, password=None, number=None, + sid=None, token=None, + proxies=None, + version='5.0', app_id=2895443, scope=2097151): + ''' + :param login: Логин ВКонтакте + :param password: Пароль ВКонтакте + :param number: Номер при проверке безопасности + Номер: +7 12345678 90 + number = 12345678 + :param sid: cookie remixsid + :param token: access_token + :param proxies: proxy server + {'http': 'http://127.0.0.1:8888/', + 'https' : 'https://127.0.0.1:8888/'} + :param version: Версия API (default: '5.0') + :param app_id: Standalone-приложение (default: 2895443) + :param scope: Запрашиваемые права (default: 2097151) + ''' + + self.login = login + self.password = password + self.number = number + + self.sid = sid + self.token = {'access_token': token} + + self.version = version + self.app_id = app_id + self.scope = scope + + self.settings = jconfig.config(login) + + self.http = requests.Session() + self.http.proxies = proxies # Ставим прокси + self.http.headers = { # Притворимся браузером + 'User-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) ' \ + 'Gecko/20100101 Firefox/20.0' + } + self.http.verify = False + + if login and password: + self.sid = self.settings['remixsid'] + self.token = self.settings['access_token'] + + if not self.check_sid(): + self.vk_login() + + if not self.check_token(): + self.api_login() + + def vk_login(self, captcha_sid=None, captcha_key=None): + ''' Авторизцаия ВКонтакте с получением cookies remixsid ''' + + url = 'https://login.vk.com/' + values = { + 'act': 'login', + 'utf8': '1', + 'email': self.login, + 'pass': self.password + } + + if captcha_sid and captcha_key: + values.update({ + 'captcha_sid': captcha_sid, + 'captcha_key': captcha_key + }) + + self.http.cookies.clear() + response = self.http.post(url, values) + + if 'remixsid' in self.http.cookies: + remixsid = self.http.cookies['remixsid'] + self.settings['remixsid'] = remixsid + + # Нужно для авторизации в API + self.settings['forapilogin'] = { + 'p': self.http.cookies['p'], + 'l': self.http.cookies['l'] + } + + self.sid = remixsid + + elif 'sid=' in response.url: + raise authorization_error('Authorization error (capcha)')\ + # TODO: write me + # Capcha handler + # Capcha object + else: + raise authorization_error('Authorization error (bad password)') + + if 'security_check' in response.url and self.number: + number_hash = regexp(r'security_check.*?hash: \'(.*?)\'\};', + response.text)[0] + + values = { + 'act': 'security_check', + 'al': '1', + 'al_page': '3', + 'code': self.number, + 'hash': number_hash, + 'to': '' + } + + response = self.http.post('https://vk.com/login.php', values) + + if response.text.split('')[4] == '4': + return + + raise authorization_error('Authorization error (enter number)') + + def check_sid(self): + ''' Прверка Cookies remixsid на валидность ''' + + if self.sid: + url = 'https://vk.com/feed2.php' + self.http.cookies.update({ + 'remixsid': self.sid, + 'remixlang': '0', + 'remixsslsid': '1' + }) + + response = self.http.get(url).json() + + if response['user']['id'] != -1: + return response + + def api_login(self): + ''' Получение токена через Desktop приложение ''' + + url = 'https://oauth.vk.com/authorize' + values = { + 'client_id': self.app_id, + 'scope': self.scope, + 'response_type': 'token', + } + + self.http.cookies.update(self.settings['forapilogin']) + self.http.cookies.update({'remixsid': self.sid}) + + response = self.http.post(url, values) + + if not 'access_token' in response.url: + url = regexp(r'location\.href = "(.*?)"\+addr;', response.text)[0] + response = self.http.get(url) + + if 'access_token' in response.url: + params = response.url.split('#')[1].split('&') + + token = {} + for i in params: + x = i.split('=') + token.update({x[0]: x[1]}) + + self.settings['access_token'] = token + self.token = token + else: + raise authorization_error('Authorization error (api)') + + def check_token(self): + ''' Прверка access_token на валидность ''' + + if self.token: + try: + self.method('isAppUser') + except api_error: + return False + + return True + + def method(self, method, values=None): + ''' Использование методов API + + method - название метода + 'users.get' + + values - параметры + {'uids': 1} + ''' + + url = 'https://api.vk.com/method/%s' % method + values = values or {} + + if not 'v' in values: + values.update({'v': self.version}) + + if self.token: + values.update({'access_token': self.token['access_token']}) + + response = self.http.post(url, values).json() + if 'error' in response: + # TODO: write me + # Capcha handler + # Capcha object + # Error #17 handler + raise api_error(response['error']) + else: + return response['response'] + + +def regexp(reg, string): + ''' Поиск по регулярке ''' + + reg = re.compile(reg, re.IGNORECASE | re.DOTALL) + reg = reg.findall(string) + return reg + + +class vk_api_error(Exception): + pass + + +class authorization_error(vk_api_error): + pass + + +class api_error(vk_api_error): + pass + + +class capcha(): + pass + diff --git a/vk_api/vk_upload.py b/vk_api/vk_upload.py index 898f2b1a..cf8dcca8 100644 --- a/vk_api/vk_upload.py +++ b/vk_api/vk_upload.py @@ -1,101 +1,101 @@ -''' -@author: Kirill Python -@contact: http://vk.com/python273 -@license Apache License, Version 2.0, see LICENSE file - -Copyright (C) 2013 -''' - -# -*- coding: utf-8 -*- - - -class VkUpload(): - def __init__(self, vk): - self.vk = vk - # https://vk.com/dev/upload_files - - def photo(self, photos, album_id=None, group_id=None): - ''' Загрузка изображений в альбом пользователя - - photos = ['photo1.jpg', 'img.png'] - = 'screen.png' - Максимум 5 фотографий - - album_id - ''' - - if not (album_id and photos): - return False - - if type(photos) == str: # upload.photo('photo.jpg', ...) - photos = [photos] - - values = { - 'album_id': album_id - } - if group_id: # Если загружаем в группу - values.update({'group_id': group_id}) - - # Получаем ссылку для загрузки - url = self.vk.method('photos.getUploadServer', values)['upload_url'] - - # Загружаем - photos_files = openPhotos(photos) - response = self.vk.http.post(url, files=photos_files).json() - closePhotos(photos_files) - - # Олег Илларионов: - # это не могу к сожалению просто пофиксить - if not 'album_id' in response: - response['album_id'] = response['aid'] - - # Сохраняем фото в альбоме - response = self.vk.method('photos.save', response) - - return response - - def photoMessages(self, photos, group_id=None): - ''' Загрузка изображений в сообщения - - photos = ['photo1.jpg', 'img.png'] - = 'screen.png' - Максимум 7(?) фотографий - ''' - - if not photos: - return False - - if type(photos) == str: # upload.photo('photo.jpg', ...) - photos = [photos] - - values = {} - if group_id: - values.update({'group_id': group_id}) - - # Получаем ссылку для загрузки - url = self.vk.method('photos.getMessagesUploadServer', values)['upload_url'] - - # Загружаем - photos_files = openPhotos(photos) - response = self.vk.http.post(url, files=photos_files) - closePhotos(photos_files) - - # Сохраняем фото в альбоме - response = self.vk.method('photos.saveMessagesPhoto', response.json()) - - return response - - -def openPhotos(photos_paths): - photos = {} - - for x, filename in enumerate(photos_paths): # Открываем файлы - photos.update( - {'file%s' % x: open(filename, 'rb')} - ) - - return photos - -def closePhotos(photos): - for i in photos: - photos[i].close() +''' +@author: Kirill Python +@contact: http://vk.com/python273 +@license Apache License, Version 2.0, see LICENSE file + +Copyright (C) 2013 +''' + +# -*- coding: utf-8 -*- + + +class VkUpload(object): + def __init__(self, vk): + self.vk = vk + # https://vk.com/dev/upload_files + + def photo(self, photos, album_id=None, group_id=None): + ''' Загрузка изображений в альбом пользователя + + photos = ['photo1.jpg', 'img.png'] + = 'screen.png' + Максимум 5 фотографий + + album_id + ''' + + if not (album_id and photos): + return False + + if type(photos) == str: # upload.photo('photo.jpg', ...) + photos = [photos] + + values = { + 'album_id': album_id + } + if group_id: # Если загружаем в группу + values.update({'group_id': group_id}) + + # Получаем ссылку для загрузки + url = self.vk.method('photos.getUploadServer', values)['upload_url'] + + # Загружаем + photos_files = openPhotos(photos) + response = self.vk.http.post(url, files=photos_files).json() + closePhotos(photos_files) + + # Олег Илларионов: + # это не могу к сожалению просто пофиксить + if not 'album_id' in response: + response['album_id'] = response['aid'] + + # Сохраняем фото в альбоме + response = self.vk.method('photos.save', response) + + return response + + def photoMessages(self, photos, group_id=None): + ''' Загрузка изображений в сообщения + + photos = ['photo1.jpg', 'img.png'] + = 'screen.png' + Максимум 7(?) фотографий + ''' + + if not photos: + return False + + if type(photos) == str: # upload.photo('photo.jpg', ...) + photos = [photos] + + values = {} + if group_id: + values.update({'group_id': group_id}) + + # Получаем ссылку для загрузки + url = self.vk.method('photos.getMessagesUploadServer', values)['upload_url'] + + # Загружаем + photos_files = openPhotos(photos) + response = self.vk.http.post(url, files=photos_files) + closePhotos(photos_files) + + # Сохраняем фото в альбоме + response = self.vk.method('photos.saveMessagesPhoto', response.json()) + + return response + + +def openPhotos(photos_paths): + photos = {} + + for x, filename in enumerate(photos_paths): # Открываем файлы + photos.update( + {'file%s' % x: open(filename, 'rb')} + ) + + return photos + +def closePhotos(photos): + for i in photos: + photos[i].close() From f000efb366f96783c959c881e067be683e30914b Mon Sep 17 00:00:00 2001 From: python273 Date: Sun, 29 Sep 2013 15:41:24 +0400 Subject: [PATCH 2/9] 4.6.2 fix example.py for new api encoding in first line --- example.py | 5 +++-- jconfig/__init__.py | 2 ++ jconfig/jconfig.py | 3 ++- setup.py | 2 ++ vk_api/__init__.py | 2 ++ vk_api/vk_upload.py | 4 ++-- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/example.py b/example.py index 90306877..5eb49661 100644 --- a/example.py +++ b/example.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 @@ -6,7 +8,6 @@ Copyright (C) 2013 ''' -# -*- coding: utf-8 -*- import vk_api @@ -26,7 +27,7 @@ def main(): 'count': 1 # Получаем только одно сообщение } response = vk.method('wall.get', values) # Используем метод wall.get - print(response[1]['text']) # Печатаем текст последнего поста со стены + print(response['items'][0]['text']) # Печатаем текст последнего поста со стены if __name__ == '__main__': main() diff --git a/jconfig/__init__.py b/jconfig/__init__.py index e8190ebe..7914d79f 100644 --- a/jconfig/__init__.py +++ b/jconfig/__init__.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 diff --git a/jconfig/jconfig.py b/jconfig/jconfig.py index bdf28bac..800b05a0 100644 --- a/jconfig/jconfig.py +++ b/jconfig/jconfig.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 @@ -6,7 +8,6 @@ Copyright (C) 2013 ''' -# -*- coding: utf-8 -*- import os import json diff --git a/setup.py b/setup.py index fab486d2..93f3df63 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 diff --git a/vk_api/__init__.py b/vk_api/__init__.py index 1f10ef6b..c0f6f94a 100644 --- a/vk_api/__init__.py +++ b/vk_api/__init__.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 diff --git a/vk_api/vk_upload.py b/vk_api/vk_upload.py index cf8dcca8..52ebb0a6 100644 --- a/vk_api/vk_upload.py +++ b/vk_api/vk_upload.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 @@ -6,8 +8,6 @@ Copyright (C) 2013 ''' -# -*- coding: utf-8 -*- - class VkUpload(object): def __init__(self, vk): From eddd791932f1418aa1103ea32704a45eea035e62 Mon Sep 17 00:00:00 2001 From: kirill Date: Mon, 25 Nov 2013 23:23:18 +0400 Subject: [PATCH 3/9] 3 request per second limit API version 5.4 Get number from login for security check vk_tools --- MANIFEST | 16 ++++----- setup.py | 8 ++--- vk_api/__init__.py | 4 ++- vk_api/vk_api.py | 88 ++++++++++++++++++++++++++++++++++++---------- vk_api/vk_tools.py | 37 +++++++++++++++++++ 5 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 vk_api/vk_tools.py diff --git a/MANIFEST b/MANIFEST index eed4a61b..5a720487 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,8 +1,8 @@ -# file GENERATED by distutils, do NOT edit -setup.py -jconfig\__init__.py -jconfig\jconfig.py -test\test.py -vk_api\__init__.py -vk_api\vk_api.py -vk_api\vk_upload.py +# file GENERATED by distutils, do NOT edit +setup.py +jconfig\__init__.py +jconfig\jconfig.py +test\test.py +vk_api\__init__.py +vk_api\vk_api.py +vk_api\vk_upload.py diff --git a/setup.py b/setup.py index 93f3df63..5555b43d 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- ''' @@ -8,13 +9,10 @@ Copyright (C) 2013 ''' -#!/usr/bin/env python -""" Setup file for vk_api package """ - from distutils.core import setup setup(name='vk_api', - version='4.6', - description='Module to use API VKontakte vk.com', + version='4.6.3', + description='Module to use API vk.com', author='Kirill Python', author_email='mikeking568@gmail.com', url='https://github.com/python273/vk_api', diff --git a/vk_api/__init__.py b/vk_api/__init__.py index c0f6f94a..28a406dc 100644 --- a/vk_api/__init__.py +++ b/vk_api/__init__.py @@ -9,7 +9,7 @@ ''' __author__ = "Kirill Python" -__version__ = "4.6" +__version__ = "4.6.3" __email__ = "mikeking568@gmail.com" __contact__ = "https://vk.com/python273" @@ -18,6 +18,8 @@ if sys.version_info[0] == 2: from vk_api import * from vk_upload import * + from vk_tools import * else: from .vk_api import * from .vk_upload import * + from .vk_tools import * diff --git a/vk_api/vk_api.py b/vk_api/vk_api.py index b213509d..2cd28237 100644 --- a/vk_api/vk_api.py +++ b/vk_api/vk_api.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + ''' @author: Kirill Python @contact: http://vk.com/python273 @@ -6,26 +8,26 @@ Copyright (C) 2013 ''' -# -*- coding: utf-8 -*- import jconfig import re import requests -# import time +import time + +DELAY = 1.0 / 3 # 3 requests per second class VkApi(object): def __init__(self, login=None, password=None, number=None, - sid=None, token=None, + token=None, proxies=None, - version='5.0', app_id=2895443, scope=2097151): + version='5.4', app_id=2895443, scope=2097151): ''' :param login: Логин ВКонтакте :param password: Пароль ВКонтакте :param number: Номер при проверке безопасности Номер: +7 12345678 90 number = 12345678 - :param sid: cookie remixsid :param token: access_token :param proxies: proxy server {'http': 'http://127.0.0.1:8888/', @@ -39,7 +41,7 @@ def __init__(self, self.password = password self.number = number - self.sid = sid + self.sid = None self.token = {'access_token': token} self.version = version @@ -56,6 +58,8 @@ def __init__(self, } self.http.verify = False + self.last_request = 0.0 + if login and password: self.sid = self.settings['remixsid'] self.token = self.settings['access_token'] @@ -106,7 +110,24 @@ def vk_login(self, captcha_sid=None, captcha_key=None): else: raise authorization_error('Authorization error (bad password)') - if 'security_check' in response.url and self.number: + if 'security_check' in response.url: + self.security_check(response) + + def security_check(self, url=None, response=None): + if url: + response = self.http.get(url) + + # Проверяем, является ли логин номером + if not self.number: + phone_postfix = regexp(r'class="phone_postfix">(.*?)', + response.text) + + phone_postfix = phone_postfix[0].strip() + + if self.login[-len(phone_postfix):] == phone_postfix: + self.number = self.login + + if self.number: number_hash = regexp(r'security_check.*?hash: \'(.*?)\'\};', response.text)[0] @@ -122,9 +143,10 @@ def vk_login(self, captcha_sid=None, captcha_key=None): response = self.http.post('https://vk.com/login.php', values) if response.text.split('')[4] == '4': - return + return True + + raise authorization_error('Security check (enter number)') - raise authorization_error('Authorization error (enter number)') def check_sid(self): ''' Прверка Cookies remixsid на валидность ''' @@ -180,7 +202,7 @@ def check_token(self): if self.token: try: self.method('isAppUser') - except api_error: + except apiError: return False return True @@ -188,15 +210,18 @@ def check_token(self): def method(self, method, values=None): ''' Использование методов API - method - название метода + param: method - название метода 'users.get' - values - параметры + param: values - параметры {'uids': 1} ''' - url = 'https://api.vk.com/method/%s' % method - values = values or {} + + if values: + values = values.copy() + else: + values = {} if not 'v' in values: values.update({'v': self.version}) @@ -204,13 +229,27 @@ def method(self, method, values=None): if self.token: values.update({'access_token': self.token['access_token']}) + # Ограничение 3 запроса в секунду + sleep = DELAY - (time.time() - self.last_request) + + if sleep > 0: + time.sleep(sleep) + response = self.http.post(url, values).json() + self.last_request = time.time() + if 'error' in response: # TODO: write me # Capcha handler # Capcha object - # Error #17 handler - raise api_error(response['error']) + + error = apiError(response['error'], self, method, values) + + if error.code == 17: + print 'HANDLER 17' + # TODO: number handler + + raise error else: return response['response'] @@ -231,10 +270,21 @@ class authorization_error(vk_api_error): pass -class api_error(vk_api_error): - pass +class apiError(Exception): + def __init__(self, error, vk, method, values): + self.error = error + self.vk = vk + self.method = method + self.values = values + self.code = error['error_code'] + + def try_method(self): + return self.vk.method(self.method, self.values) + + def __str__(self): + return self.error['error_msg'] -class capcha(): +class Capcha(): pass diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py new file mode 100644 index 00000000..4a2cd28c --- /dev/null +++ b/vk_api/vk_tools.py @@ -0,0 +1,37 @@ +# encoding: utf-8 + + +class VkTools(object): + def __init__(self, vk): + self.vk = vk + + def get_all_items(self, method, values=None, max_count=200): + ''' Получить все элементы + Работает в методах, где в ответе есть count и items + + :param method: метод + :param values: параметры + :param max_count: максимальное количество, + которое можно получить за один раз + ''' + + if not values: + values = {} + else: + values = values.copy() + + values.update({'count': max_count}) + + response = self.vk.method(method, values) + count = response['count'] + items = response['items'] + + for i in xrange(max_count, count + 1, max_count): + values.update({ + 'offset': i + }) + + response = self.vk.method(method, values) + items += response['items'] + + return {'count': len(items), 'items': items} From ed812f955460df997493e712c295033110399a5e Mon Sep 17 00:00:00 2001 From: kirill Date: Fri, 29 Nov 2013 13:22:40 +0400 Subject: [PATCH 4/9] 4.6.4 Fast VkTools.get_all_items --- jconfig/jconfig.py | 2 +- setup.py | 2 +- vk_api/__init__.py | 2 +- vk_api/vk_api.py | 2 +- vk_api/vk_tools.py | 43 ++++++++++++++++++++++++++++++++++++++++++- vk_procedures | 30 ++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 vk_procedures diff --git a/jconfig/jconfig.py b/jconfig/jconfig.py index 800b05a0..8b433b05 100644 --- a/jconfig/jconfig.py +++ b/jconfig/jconfig.py @@ -12,7 +12,7 @@ import json -class config(object): +class Config(object): def __init__(self, section, filename='config'): self.section = section # Секция настроек self.filename = filename # Файл с настройками diff --git a/setup.py b/setup.py index 5555b43d..395edb6b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from distutils.core import setup setup(name='vk_api', - version='4.6.3', + version='4.6.4', description='Module to use API vk.com', author='Kirill Python', author_email='mikeking568@gmail.com', diff --git a/vk_api/__init__.py b/vk_api/__init__.py index 28a406dc..87845ace 100644 --- a/vk_api/__init__.py +++ b/vk_api/__init__.py @@ -9,7 +9,7 @@ ''' __author__ = "Kirill Python" -__version__ = "4.6.3" +__version__ = "4.6.4" __email__ = "mikeking568@gmail.com" __contact__ = "https://vk.com/python273" diff --git a/vk_api/vk_api.py b/vk_api/vk_api.py index 2cd28237..077e8078 100644 --- a/vk_api/vk_api.py +++ b/vk_api/vk_api.py @@ -48,7 +48,7 @@ def __init__(self, self.app_id = app_id self.scope = scope - self.settings = jconfig.config(login) + self.settings = jconfig.Config(login) self.http = requests.Session() self.http.proxies = proxies # Ставим прокси diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py index 4a2cd28c..5521e141 100644 --- a/vk_api/vk_tools.py +++ b/vk_api/vk_tools.py @@ -1,4 +1,5 @@ # encoding: utf-8 +import json class VkTools(object): @@ -8,10 +9,47 @@ def __init__(self, vk): def get_all_items(self, method, values=None, max_count=200): ''' Получить все элементы Работает в методах, где в ответе есть count и items + За один запрос получает max_count * 25 элементов :param method: метод :param values: параметры - :param max_count: максимальное количество, + :param max_count: максимальное количество элементов, + которое можно получить за один раз + ''' + + if values: + values = values.copy() + else: + values = {} + + items = [] + offset = 0 + + while True: + run_code = code_get_all_items % (max_count, offset, json.dumps(values), method, method) + + try: + response = self.vk.method('execute', {'code': run_code}) + except Exception as e: + print run_code + print e + + items += response['items'] + + if response['end']: + break + + offset = response['offset'] + + return {'count': len(items), 'items': items} + + def get_all_items_slow(self, method, values=None, max_count=200): + ''' Получить все элементы + Работает в методах, где в ответе есть count и items + + :param method: метод + :param values: параметры + :param max_count: максимальное количество элементов, которое можно получить за один раз ''' @@ -35,3 +73,6 @@ def get_all_items(self, method, values=None, max_count=200): items += response['items'] return {'count': len(items), 'items': items} + +# Полный код в файле vk_procedures +code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=z,i=1;while(i<25&&o+z=c};' diff --git a/vk_procedures b/vk_procedures new file mode 100644 index 00000000..92d4fe8b --- /dev/null +++ b/vk_procedures @@ -0,0 +1,30 @@ +========== +CODE: get items x25 +INPUT: max_count, start_offset, params:in_JSON, method, method +---------- +var max_count = %s; +var start_offset = %s; +var my_params = %s; + +var params = {"count": max_count} + my_params; + +var res = API.%s(params); +var count = res["count"]; +var items = res["items"]; + +var offset = max_count; + +var i = 1; +while(i < 25 && offset + max_count < count) { + offset = i * max_count + start_offset; + params = {"count": max_count, "offset": offset}; + params = params + my_params; + + res = API.%s(params); + items = items + res["items"]; + + i = i + 1; +} + +return {"count": count, "items": items, "offset": offset, "end": offset + max_count >= count}; +========== \ No newline at end of file From 48994e30407971c65ba6689445563ea60750ac1b Mon Sep 17 00:00:00 2001 From: Kirill Python Date: Sat, 14 Dec 2013 14:46:19 +0400 Subject: [PATCH 5/9] Fix bug in get_all_items --- vk_api/vk_tools.py | 2 +- vk_procedures | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py index 5521e141..cfa489f5 100644 --- a/vk_api/vk_tools.py +++ b/vk_api/vk_tools.py @@ -75,4 +75,4 @@ def get_all_items_slow(self, method, values=None, max_count=200): return {'count': len(items), 'items': items} # Полный код в файле vk_procedures -code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=z,i=1;while(i<25&&o+z=c};' +code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=z,i=1;while(i<25&&o=c};' diff --git a/vk_procedures b/vk_procedures index 92d4fe8b..c6e0555e 100644 --- a/vk_procedures +++ b/vk_procedures @@ -15,7 +15,7 @@ var items = res["items"]; var offset = max_count; var i = 1; -while(i < 25 && offset + max_count < count) { +while(i < 25 && offset < count) { offset = i * max_count + start_offset; params = {"count": max_count, "offset": offset}; params = params + my_params; @@ -26,5 +26,5 @@ while(i < 25 && offset + max_count < count) { i = i + 1; } -return {"count": count, "items": items, "offset": offset, "end": offset + max_count >= count}; +return {"count": count, "items": items, "offset": offset, "end": offset >= count}; ========== \ No newline at end of file From 0c98eda8f846e4dc48eefc801bb229a2a0c84ef1 Mon Sep 17 00:00:00 2001 From: Kirill Python Date: Sat, 14 Dec 2013 15:20:33 +0400 Subject: [PATCH 6/9] Fix bug in get_all_items #2 --- setup.py | 2 +- vk_api/__init__.py | 2 +- vk_api/vk_tools.py | 2 +- vk_procedures | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 395edb6b..fd0dc8f4 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from distutils.core import setup setup(name='vk_api', - version='4.6.4', + version='4.6.5', description='Module to use API vk.com', author='Kirill Python', author_email='mikeking568@gmail.com', diff --git a/vk_api/__init__.py b/vk_api/__init__.py index 87845ace..5d4b052f 100644 --- a/vk_api/__init__.py +++ b/vk_api/__init__.py @@ -9,7 +9,7 @@ ''' __author__ = "Kirill Python" -__version__ = "4.6.4" +__version__ = "4.6.5" __email__ = "mikeking568@gmail.com" __contact__ = "https://vk.com/python273" diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py index cfa489f5..734e06bc 100644 --- a/vk_api/vk_tools.py +++ b/vk_api/vk_tools.py @@ -75,4 +75,4 @@ def get_all_items_slow(self, method, values=None, max_count=200): return {'count': len(items), 'items': items} # Полный код в файле vk_procedures -code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=z,i=1;while(i<25&&o=c};' +code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=0,i=1;while(i<25&&o=c};' diff --git a/vk_procedures b/vk_procedures index c6e0555e..582220c1 100644 --- a/vk_procedures +++ b/vk_procedures @@ -12,7 +12,7 @@ var res = API.%s(params); var count = res["count"]; var items = res["items"]; -var offset = max_count; +var offset = 0; var i = 1; while(i < 25 && offset < count) { From 1d89e5e44dc5cd4a815427d0ce5cd54b7e534fdb Mon Sep 17 00:00:00 2001 From: Kirill Python Date: Sat, 28 Dec 2013 03:52:05 +0400 Subject: [PATCH 7/9] VkTools.get_all_items rename to VkTools.get_all Add "key" param in VkTools.get_all --- vk_api/vk_tools.py | 31 ++++++++++++++++--------------- vk_procedures | 7 ++++--- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py index 734e06bc..18e5c358 100644 --- a/vk_api/vk_tools.py +++ b/vk_api/vk_tools.py @@ -6,15 +6,16 @@ class VkTools(object): def __init__(self, vk): self.vk = vk - def get_all_items(self, method, values=None, max_count=200): + def get_all(self, method, values=None, max_count=200, key='items'): ''' Получить все элементы - Работает в методах, где в ответе есть count и items + Работает в методах, где в ответе есть items или users За один запрос получает max_count * 25 элементов :param method: метод :param values: параметры :param max_count: максимальное количество элементов, которое можно получить за один раз + :param key: ключ элементов, которые нужно получить ''' if values: @@ -26,13 +27,12 @@ def get_all_items(self, method, values=None, max_count=200): offset = 0 while True: - run_code = code_get_all_items % (max_count, offset, json.dumps(values), method, method) + run_code = code_get_all_items % ( + max_count, offset, json.dumps(values), + key, method, method + ) - try: - response = self.vk.method('execute', {'code': run_code}) - except Exception as e: - print run_code - print e + response = self.vk.method('execute', {'code': run_code}) items += response['items'] @@ -41,16 +41,17 @@ def get_all_items(self, method, values=None, max_count=200): offset = response['offset'] - return {'count': len(items), 'items': items} + return {'count': len(items), key: items} - def get_all_items_slow(self, method, values=None, max_count=200): + def get_all_slow(self, method, values=None, max_count=200, key='items'): ''' Получить все элементы - Работает в методах, где в ответе есть count и items + Работает в методах, где в ответе есть count и items или users :param method: метод :param values: параметры :param max_count: максимальное количество элементов, которое можно получить за один раз + :param key: ключ элементов, которые нужно получить ''' if not values: @@ -62,7 +63,7 @@ def get_all_items_slow(self, method, values=None, max_count=200): response = self.vk.method(method, values) count = response['count'] - items = response['items'] + items = response[key] for i in xrange(max_count, count + 1, max_count): values.update({ @@ -70,9 +71,9 @@ def get_all_items_slow(self, method, values=None, max_count=200): }) response = self.vk.method(method, values) - items += response['items'] + items += response[key] - return {'count': len(items), 'items': items} + return {'count': len(items), key: items} # Полный код в файле vk_procedures -code_get_all_items = 'var z=%s,x=%s,y=%s,p={"count":z}+y,r=API.%s(p),c=r["count"],j=r["items"],o=0,i=1;while(i<25&&o=c};' +code_get_all_items = 'var z=%s,x=%s,y=%s,k="%s",p={"count":z}+y,r=API.%s(p),c=r["count"],j=r[k],o=0,i=1;while(i<25&&o=c};' diff --git a/vk_procedures b/vk_procedures index 582220c1..09771108 100644 --- a/vk_procedures +++ b/vk_procedures @@ -1,16 +1,17 @@ ========== CODE: get items x25 -INPUT: max_count, start_offset, params:in_JSON, method, method +INPUT: max_count, start_offset, params:in_JSON, key, method, method ---------- var max_count = %s; var start_offset = %s; var my_params = %s; +var key = %s; var params = {"count": max_count} + my_params; var res = API.%s(params); var count = res["count"]; -var items = res["items"]; +var items = res[key]; var offset = 0; @@ -21,7 +22,7 @@ while(i < 25 && offset < count) { params = params + my_params; res = API.%s(params); - items = items + res["items"]; + items = items + res[key]; i = i + 1; } From 9e24f5f32626f32792909442784d22eb59fdbccf Mon Sep 17 00:00:00 2001 From: Kirill Python Date: Sat, 28 Dec 2013 03:56:34 +0400 Subject: [PATCH 8/9] Version: 4.6.6 API version: 5.5 --- setup.py | 2 +- vk_api/__init__.py | 2 +- vk_api/vk_api.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index fd0dc8f4..2db79456 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from distutils.core import setup setup(name='vk_api', - version='4.6.5', + version='4.6.6', description='Module to use API vk.com', author='Kirill Python', author_email='mikeking568@gmail.com', diff --git a/vk_api/__init__.py b/vk_api/__init__.py index 5d4b052f..52432289 100644 --- a/vk_api/__init__.py +++ b/vk_api/__init__.py @@ -9,7 +9,7 @@ ''' __author__ = "Kirill Python" -__version__ = "4.6.5" +__version__ = "4.6.6" __email__ = "mikeking568@gmail.com" __contact__ = "https://vk.com/python273" diff --git a/vk_api/vk_api.py b/vk_api/vk_api.py index 077e8078..bc375201 100644 --- a/vk_api/vk_api.py +++ b/vk_api/vk_api.py @@ -21,7 +21,7 @@ def __init__(self, login=None, password=None, number=None, token=None, proxies=None, - version='5.4', app_id=2895443, scope=2097151): + version='5.5', app_id=2895443, scope=2097151): ''' :param login: Логин ВКонтакте :param password: Пароль ВКонтакте From 0a1efddadf62a0c1ba266b1a9d712986b30cfba0 Mon Sep 17 00:00:00 2001 From: Kirill Python Date: Mon, 14 Apr 2014 21:37:56 +0400 Subject: [PATCH 9/9] 4.7 --- README.md | 2 +- example.py | 9 +- setup.py | 2 +- vk_api/vk_api.py | 208 ++++++++++++++++++++++++++++++++------------- vk_api/vk_tools.py | 4 + 5 files changed, 161 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 8c430eff..1f94466a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ vk_api **vk_api** - модуль для использования API сайта ВКонтакте (vk.com). Пример смотрите в файле [example.py](https://github.com/python273/vk_api/blob/master/example.py) -Тестовая поддержка **Python 3**! +Python 2 / 3 С вопросами или советами можете [написать автору в ВК](https://vk.com/im?sel=183433824). diff --git a/example.py b/example.py index 5eb49661..74244589 100644 --- a/example.py +++ b/example.py @@ -19,15 +19,18 @@ def main(): try: vk = vk_api.VkApi(login, password) # Авторизируемся - except vk_api.authorization_error as error_msg: + except vk_api.AuthorizationError as error_msg: print(error_msg) # В случае ошибки выведем сообщение return # и выйдем values = { - 'count': 1 # Получаем только одно сообщение + 'count': 1 # Получаем только один пост } response = vk.method('wall.get', values) # Используем метод wall.get - print(response['items'][0]['text']) # Печатаем текст последнего поста со стены + + if response['items']: + # Печатаем текст последнего поста со стены + print(response['items'][0]['text']) if __name__ == '__main__': main() diff --git a/setup.py b/setup.py index 2db79456..895ca33d 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from distutils.core import setup setup(name='vk_api', - version='4.6.6', + version='4.7', description='Module to use API vk.com', author='Kirill Python', author_email='mikeking568@gmail.com', diff --git a/vk_api/vk_api.py b/vk_api/vk_api.py index bc375201..3b6a58d1 100644 --- a/vk_api/vk_api.py +++ b/vk_api/vk_api.py @@ -14,25 +14,25 @@ import time DELAY = 1.0 / 3 # 3 requests per second +CAPTCHA_ERROR_CODE = 14 +NEED_VALIDATION_CODE = 17 class VkApi(object): - def __init__(self, - login=None, password=None, number=None, - token=None, - proxies=None, - version='5.5', app_id=2895443, scope=2097151): + def __init__(self, login=None, password=None, number=None, token=None, + proxies=None, captcha_handler=None, + api_version='5.21', app_id=2895443, scope=2097151): ''' :param login: Логин ВКонтакте :param password: Пароль ВКонтакте - :param number: Номер при проверке безопасности - Номер: +7 12345678 90 - number = 12345678 + :param number: Номер для проверке безопасности (указывать, если + в качестве логина используется не номер) + :param token: access_token :param proxies: proxy server {'http': 'http://127.0.0.1:8888/', - 'https' : 'https://127.0.0.1:8888/'} - :param version: Версия API (default: '5.0') + 'https': 'https://127.0.0.1:8888/'} + :param api_version: Версия API (default: '5.21') :param app_id: Standalone-приложение (default: 2895443) :param scope: Запрашиваемые права (default: 2097151) ''' @@ -44,7 +44,7 @@ def __init__(self, self.sid = None self.token = {'access_token': token} - self.version = version + self.api_version = api_version self.app_id = app_id self.scope = scope @@ -53,19 +53,26 @@ def __init__(self, self.http = requests.Session() self.http.proxies = proxies # Ставим прокси self.http.headers = { # Притворимся браузером - 'User-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:20.0) ' \ - 'Gecko/20100101 Firefox/20.0' + 'User-agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:28.0) ' \ + 'Gecko/20100101 Firefox/28.0' } self.http.verify = False self.last_request = 0.0 + self.error_handlers = { + NEED_VALIDATION_CODE: self.need_validation_handler, + CAPTCHA_ERROR_CODE: captcha_handler or self.captcha_handler + } + if login and password: self.sid = self.settings['remixsid'] self.token = self.settings['access_token'] if not self.check_sid(): self.vk_login() + else: + self.security_check('http://vk.com') if not self.check_token(): self.api_login() @@ -103,31 +110,39 @@ def vk_login(self, captcha_sid=None, captcha_key=None): self.sid = remixsid elif 'sid=' in response.url: - raise authorization_error('Authorization error (capcha)')\ - # TODO: write me - # Capcha handler - # Capcha object + captcha_sid = regexp(r'sid=(\d+)', response.url)[0] + captcha = Captcha(self, captcha_sid, self.vk_login) + + if self.error_handlers[CAPTCHA_ERROR_CODE]: + self.error_handlers[CAPTCHA_ERROR_CODE](captcha) + else: + raise AuthorizationError('Authorization error (capcha)') else: - raise authorization_error('Authorization error (bad password)') + raise BadPassword('Bad password') if 'security_check' in response.url: - self.security_check(response) + self.security_check(response=response) def security_check(self, url=None, response=None): if url: response = self.http.get(url) + if not 'security_check' in response.url: + return - # Проверяем, является ли логин номером - if not self.number: - phone_postfix = regexp(r'class="phone_postfix">(.*?)', - response.text) - - phone_postfix = phone_postfix[0].strip() + phone_prefix = regexp(r'label ta_r">(.*?)<', + response.text) + phone_prefix = phone_prefix[0].strip() - if self.login[-len(phone_postfix):] == phone_postfix: - self.number = self.login + phone_postfix = regexp(r'phone_postfix">(.*?)<', + response.text) + phone_postfix = phone_postfix[0].strip() if self.number: + code = code_from_number(phone_prefix, phone_postfix, self.number) + else: + code = code_from_number(phone_prefix, phone_postfix, self.login) + + if code: number_hash = regexp(r'security_check.*?hash: \'(.*?)\'\};', response.text)[0] @@ -135,7 +150,7 @@ def security_check(self, url=None, response=None): 'act': 'security_check', 'al': '1', 'al_page': '3', - 'code': self.number, + 'code': code, 'hash': number_hash, 'to': '' } @@ -145,8 +160,7 @@ def security_check(self, url=None, response=None): if response.text.split('')[4] == '4': return True - raise authorization_error('Security check (enter number)') - + raise SecurityCheck('Enter number') def check_sid(self): ''' Прверка Cookies remixsid на валидность ''' @@ -194,7 +208,7 @@ def api_login(self): self.settings['access_token'] = token self.token = token else: - raise authorization_error('Authorization error (api)') + raise AuthorizationError('Authorization error (api)') def check_token(self): ''' Прверка access_token на валидность ''' @@ -202,20 +216,30 @@ def check_token(self): if self.token: try: self.method('isAppUser') - except apiError: + except ApiError: return False return True - def method(self, method, values=None): - ''' Использование методов API + def captcha_handler(self, captcha): + ''' http://vk.com/dev/captcha_error ''' + pass - param: method - название метода - 'users.get' + def need_validation_handler(self, error): + ''' http://vk.com/dev/need_validation ''' + # TODO: write me + pass + + def method(self, method, values=None, captcha_sid=None, captcha_key=None): + ''' + Использование методов API - param: values - параметры - {'uids': 1} + :param method: метод + :param values: параметры + :param captcha_sid: + :param captcha_key: ''' + url = 'https://api.vk.com/method/%s' % method if values: @@ -224,30 +248,46 @@ def method(self, method, values=None): values = {} if not 'v' in values: - values.update({'v': self.version}) + values.update({'v': self.api_version}) if self.token: values.update({'access_token': self.token['access_token']}) + if captcha_sid and captcha_key: + values.update({ + 'captcha_sid': captcha_sid, + 'captcha_key': captcha_key + }) + # Ограничение 3 запроса в секунду - sleep = DELAY - (time.time() - self.last_request) + delay = DELAY - (time.time() - self.last_request) - if sleep > 0: - time.sleep(sleep) + if delay > 0: + time.sleep(delay) response = self.http.post(url, values).json() self.last_request = time.time() if 'error' in response: - # TODO: write me - # Capcha handler - # Capcha object - - error = apiError(response['error'], self, method, values) - - if error.code == 17: - print 'HANDLER 17' - # TODO: number handler + error = ApiError(self, method, values, response['error']) + error_code = error.code + + if error_code in self.error_handlers: + if error_code == CAPTCHA_ERROR_CODE: + # TODO: wtf + error = Captcha( + self, + error.error['captcha_sid'], + self.method, + (method,), + {'values': values}, + error.error['captcha_img'] + ) + + response = self.error_handlers[error_code](error) + + if response is not None: + return response raise error else: @@ -262,29 +302,79 @@ def regexp(reg, string): return reg -class vk_api_error(Exception): +def code_from_number(phone_prefix, phone_postfix, number): + prefix_len = len(phone_prefix) + postfix_len = len(phone_postfix) + + if (prefix_len + postfix_len) > len(number): + return + + # Сравниваем начало номера + if not number[:prefix_len] == phone_prefix: + return + + # Сравниваем конец номера + if not number[-postfix_len:] == phone_postfix: + return + + return number[prefix_len:-postfix_len] + + +class AuthorizationError(Exception): pass -class authorization_error(vk_api_error): +class BadPassword(AuthorizationError): pass -class apiError(Exception): - def __init__(self, error, vk, method, values): - self.error = error +class SecurityCheck(AuthorizationError): + pass + + +class ApiError(Exception): + def __init__(self, vk, method, values, error): self.vk = vk self.method = method self.values = values self.code = error['error_code'] + self.error = error def try_method(self): return self.vk.method(self.method, self.values) def __str__(self): - return self.error['error_msg'] + return '[{}] {}'.format(self.error['error_code'], + self.error['error_msg']) -class Capcha(): - pass +class Captcha(Exception): + def __init__(self, vk, captcha_sid, + func, args=None, kwargs=None, url=None): + self.vk = vk + self.sid = captcha_sid + self.func = func + self.args = args or () + self.kwargs = kwargs or {} + + self.key = None + self.url = url + + def get_url(self): + if not self.url: + self.url = 'http://api.vk.com/captcha.php?sid={}'.format(self.sid) + + return self.url + def try_again(self, key): + self.key = key + + self.kwargs.update({ + 'captcha_sid': self.sid, + 'captcha_key': self.key + }) + + return self.func(*self.args, **self.kwargs) + + def __str__(self): + return 'Captcha needed' diff --git a/vk_api/vk_tools.py b/vk_api/vk_tools.py index 18e5c358..507e68f8 100644 --- a/vk_api/vk_tools.py +++ b/vk_api/vk_tools.py @@ -1,5 +1,9 @@ # encoding: utf-8 import json +import sys + +if sys.version_info[0] == 3: + xrange = range class VkTools(object):