From 6496fc3e1b5e331773a78845319c83510edbd471 Mon Sep 17 00:00:00 2001 From: inpos Date: Mon, 30 Jan 2017 23:13:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=80=D0=B3=D0=B0=D0=BD=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script.module.vk/addon.xml | 21 +++ script.module.vk/lib/vk/__init__.py | 9 + script.module.vk/lib/vk/__init__.pyc | Bin 0 -> 434 bytes script.module.vk/lib/vk/api.py | 185 +++++++++++++++++++++ script.module.vk/lib/vk/api.pyc | Bin 0 -> 7341 bytes script.module.vk/lib/vk/exceptions.py | 57 +++++++ script.module.vk/lib/vk/exceptions.pyc | Bin 0 -> 2910 bytes script.module.vk/lib/vk/logs.py | 27 +++ script.module.vk/lib/vk/logs.pyc | Bin 0 -> 640 bytes script.module.vk/lib/vk/mixins.py | 218 +++++++++++++++++++++++++ script.module.vk/lib/vk/mixins.pyc | Bin 0 -> 7644 bytes script.module.vk/lib/vk/tests.py | 60 +++++++ script.module.vk/lib/vk/tests.pyc | Bin 0 -> 3048 bytes script.module.vk/lib/vk/utils.py | 80 +++++++++ script.module.vk/lib/vk/utils.pyc | Bin 0 -> 2688 bytes 15 files changed, 657 insertions(+) create mode 100644 script.module.vk/addon.xml create mode 100644 script.module.vk/lib/vk/__init__.py create mode 100644 script.module.vk/lib/vk/__init__.pyc create mode 100644 script.module.vk/lib/vk/api.py create mode 100644 script.module.vk/lib/vk/api.pyc create mode 100644 script.module.vk/lib/vk/exceptions.py create mode 100644 script.module.vk/lib/vk/exceptions.pyc create mode 100644 script.module.vk/lib/vk/logs.py create mode 100644 script.module.vk/lib/vk/logs.pyc create mode 100644 script.module.vk/lib/vk/mixins.py create mode 100644 script.module.vk/lib/vk/mixins.pyc create mode 100644 script.module.vk/lib/vk/tests.py create mode 100644 script.module.vk/lib/vk/tests.pyc create mode 100644 script.module.vk/lib/vk/utils.py create mode 100644 script.module.vk/lib/vk/utils.pyc diff --git a/script.module.vk/addon.xml b/script.module.vk/addon.xml new file mode 100644 index 0000000..9aeeca7 --- /dev/null +++ b/script.module.vk/addon.xml @@ -0,0 +1,21 @@ + + + + + + + + all + + vk.com API Python wrapper + vk.com API Python wrapper + Code taken from https://pypi.python.org/pypi/vk/ + MIT License + https://pypi.python.org/pypi/vk/ + https://pypi.python.org/pypi/vk/ + + \ No newline at end of file diff --git a/script.module.vk/lib/vk/__init__.py b/script.module.vk/lib/vk/__init__.py new file mode 100644 index 0000000..052fb16 --- /dev/null +++ b/script.module.vk/lib/vk/__init__.py @@ -0,0 +1,9 @@ + +from vk.api import logger +from vk.api import Session, AuthSession, InteractiveSession, InteractiveAuthSession +from vk.api import VERSION +from vk.api import API + +__version__ = version = VERSION + +# API = OAuthAPI diff --git a/script.module.vk/lib/vk/__init__.pyc b/script.module.vk/lib/vk/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e903b489121d2a4ec8b31c068f02b3ffa5d47c28 GIT binary patch literal 434 zcmY+A%}&EG49A^*jv=Iq0}sH3UbemfLU7>F%YaluD3>tSsDw6cS<_1J0=ykB!vlcb zZUQ35@y~JWzv6x!%|Bm19yYLB4xX2|yGL|68iNYJFc1tFu`{v*!U5xgaKU&WJTN{8 zf2wfDiU33~u{UxEA_S%a@Wc$n0pvbpaezbgH?EjrWC^RLDAYFgh(mOl>N?%j_7LN% z-aha77&)!2+O9WN4{D!2`JevtVj4j$@2`25-mIpCOMP$CRXoRFR4|mwb*nidaFWI8 zPdL?aQpJ68Bz4pz<)F5tP|8v7j$*urU)P->Cf2k`tJ}p>ud94<_fou= 12: + self.censored_access_token = '{}***{}'.format(value[:4], value[-4:]) + else: + self.censored_access_token = value + logger.debug('access_token = %r', self.censored_access_token) + self.access_token_is_needed = not self._access_token + + def get_user_login(self): + logger.debug('Do nothing to get user login') + + def get_access_token(self): + """ + Dummy method + """ + logger.debug('API.get_access_token()') + return self._access_token + + def make_request(self, method_request, captcha_response=None): + + logger.debug('Prepare API Method request') + + response = self.send_api_request(method_request, captcha_response=captcha_response) + # todo Replace with something less exceptional + response.raise_for_status() + + # there are may be 2 dicts in one JSON + # for example: "{'error': ...}{'response': ...}" + for response_or_error in json_iter_parse(response.text): + if 'response' in response_or_error: + # todo Can we have error and response simultaneously + # for error in errors: + # logger.warning(str(error)) + + return response_or_error['response'] + + elif 'error' in response_or_error: + error_data = response_or_error['error'] + error = VkAPIError(error_data) + + if error.is_captcha_needed(): + captcha_key = self.get_captcha_key(error.captcha_img) + if not captcha_key: + raise error + + captcha_response = { + 'sid': error.captcha_sid, + 'key': captcha_key, + } + return self.make_request(method_request, captcha_response=captcha_response) + + elif error.is_access_token_incorrect(): + logger.info('Authorization failed. Access token will be dropped') + self.access_token = None + return self.make_request(method_request) + + else: + raise error + + def send_api_request(self, request, captcha_response=None): + url = self.API_URL + request._method_name + method_args = request._api._method_default_args.copy() + method_args.update(stringify_values(request._method_args)) + access_token = self.access_token + if access_token: + method_args['access_token'] = access_token + if captcha_response: + method_args['captcha_sid'] = captcha_response['sid'] + method_args['captcha_key'] = captcha_response['key'] + timeout = request._api._timeout + response = self.requests_session.post(url, method_args, timeout=timeout) + return response + + def get_captcha_key(self, captcha_image_url): + """ + Default behavior on CAPTCHA is to raise exception + Reload this in child + """ + return None + + def auth_code_is_needed(self, content, session): + """ + Default behavior on 2-AUTH CODE is to raise exception + Reload this in child + """ + raise VkAuthError('Authorization error (2-factor code is needed)') + + def auth_captcha_is_needed(self, content, session): + """ + Default behavior on CAPTCHA is to raise exception + Reload this in child + """ + raise VkAuthError('Authorization error (captcha)') + + def phone_number_is_needed(self, content, session): + """ + Default behavior on PHONE NUMBER is to raise exception + Reload this in child + """ + logger.error('Authorization error (phone number is needed)') + raise VkAuthError('Authorization error (phone number is needed)') + + +class API(object): + def __init__(self, session, timeout=10, **method_default_args): + self._session = session + self._timeout = timeout + self._method_default_args = method_default_args + + def __getattr__(self, method_name): + return Request(self, method_name) + + def __call__(self, method_name, **method_kwargs): + return getattr(self, method_name)(**method_kwargs) + + +class Request(object): + __slots__ = ('_api', '_method_name', '_method_args') + + def __init__(self, api, method_name): + self._api = api + self._method_name = method_name + + def __getattr__(self, method_name): + return Request(self._api, self._method_name + '.' + method_name) + + def __call__(self, **method_args): + self._method_args = method_args + return self._api._session.make_request(self) + + +class AuthSession(AuthMixin, Session): + pass + + +class InteractiveSession(InteractiveMixin, Session): + pass + + +class InteractiveAuthSession(InteractiveMixin, AuthSession): + pass diff --git a/script.module.vk/lib/vk/api.pyc b/script.module.vk/lib/vk/api.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d57dad06ef46e12271b14716c6e994a1b6676464 GIT binary patch literal 7341 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 F_Ya>$mYM(n literal 0 HcmV?d00001 diff --git a/script.module.vk/lib/vk/mixins.py b/script.module.vk/lib/vk/mixins.py new file mode 100644 index 0000000..f8c40a4 --- /dev/null +++ b/script.module.vk/lib/vk/mixins.py @@ -0,0 +1,218 @@ +# coding=utf8 + +import logging + +from vk.exceptions import VkAuthError +from vk.utils import raw_input, get_url_query, LoggingSession, get_form_action + + +logger = logging.getLogger('vk') + + +class AuthMixin(object): + LOGIN_URL = 'https://m.vk.com' + # REDIRECT_URI = 'https://oauth.vk.com/blank.html' + AUTHORIZE_URL = 'https://oauth.vk.com/authorize' + CAPTCHA_URI = 'https://m.vk.com/captcha.php' + + def __init__(self, app_id=None, user_login='', user_password='', scope='offline', **kwargs): + logger.debug('AuthMixin.__init__(app_id=%(app_id)r, user_login=%(user_login)r, user_password=%(user_password)r, **kwargs=%(kwargs)s)', + dict(app_id=app_id, user_login=user_login, user_password=user_password, kwargs=kwargs)) + + super(AuthMixin, self).__init__(**kwargs) + + self.app_id = app_id + self.user_login = user_login + self.user_password = user_password + self.scope = scope + + # Some API methods get args (e.g. user id) from access token. + # If we define user login, we need get access token now. + if self.user_login: + self.access_token = self.get_access_token() + + @property + def user_login(self): + if not self._user_login: + self._user_login = self.get_user_login() + return self._user_login + + @user_login.setter + def user_login(self, value): + self._user_login = value + + def get_user_login(self): + return self._user_login + + @property + def user_password(self): + if not self._user_password: + self._user_password = self.get_user_password() + return self._user_password + + @user_password.setter + def user_password(self, value): + self._user_password = value + + def get_user_password(self): + return self._user_password + + def get_access_token(self): + """ + Get access token using app id and user login and password. + """ + logger.debug('AuthMixin.get_access_token()') + + auth_session = LoggingSession() + with auth_session as self.auth_session: + self.auth_session = auth_session + self.login() + auth_response_url_query = self.oauth2_authorization() + + if 'access_token' in auth_response_url_query: + return auth_response_url_query['access_token'] + else: + raise VkAuthError('OAuth2 authorization error') + + def login(self): + """ + Login + """ + + response = self.auth_session.get(self.LOGIN_URL) + login_form_action = get_form_action(response.text) + if not login_form_action: + raise VkAuthError('VK changed login flow') + + login_form_data = { + 'email': self.user_login, + 'pass': self.user_password, + } + response = self.auth_session.post(login_form_action, login_form_data) + logger.debug('Cookies: %s', self.auth_session.cookies) + + response_url_query = get_url_query(response.url) + + if 'remixsid' in self.auth_session.cookies or 'remixsid6' in self.auth_session.cookies: + return + + if 'sid' in response_url_query: + self.auth_captcha_is_needed(response, login_form_data) + elif response_url_query.get('act') == 'authcheck': + self.auth_check_is_needed(response.text) + elif 'security_check' in response_url_query: + self.phone_number_is_needed(response.text) + else: + message = 'Authorization error (incorrect password)' + logger.error(message) + raise VkAuthError(message) + + def oauth2_authorization(self): + """ + OAuth2 + """ + auth_data = { + 'client_id': self.app_id, + 'display': 'mobile', + 'response_type': 'token', + 'scope': self.scope, + 'v': '5.28', + } + response = self.auth_session.post(self.AUTHORIZE_URL, auth_data) + response_url_query = get_url_query(response.url) + if 'access_token' in response_url_query: + return response_url_query + + # Permissions is needed + logger.info('Getting permissions') + # form_action = re.findall(r'
', auth_response.text)[0] + form_action = get_form_action(response.text) + logger.debug('Response form action: %s', form_action) + if form_action: + response = self.auth_session.get(form_action) + response_url_query = get_url_query(response.url) + return response_url_query + + try: + response_json = response.json() + except ValueError: # not JSON in response + error_message = 'OAuth2 grant access error' + else: + error_message = 'VK error: [{}] {}'.format(response_json['error'], response_json['error_description']) + logger.error('Permissions obtained') + raise VkAuthError(error_message) + + def auth_check_is_needed(self, html): + logger.info('User enabled 2 factors authorization. Auth check code is needed') + auth_check_form_action = get_form_action(html) + auth_check_code = self.get_auth_check_code() + auth_check_data = { + 'code': auth_check_code, + '_ajax': '1', + 'remember': '1' + } + response = self.auth_session.post(auth_check_form_action, data=auth_check_data) + + def auth_captcha_is_needed(self, response, login_form_data): + logger.info('Captcha is needed') + + response_url_dict = get_url_query(response.url) + + # form_url = re.findall(r'', response.text) + captcha_form_action = get_form_action(response.text) + logger.debug('form_url %s', captcha_form_action) + if not captcha_form_action: + raise VkAuthError('Cannot find form url') + + # todo Are we sure that `response_url_dict` doesn't contain CAPTCHA image url? + captcha_url = '%s?s=%s&sid=%s' % (self.CAPTCHA_URI, response_url_dict['s'], response_url_dict['sid']) + # logger.debug('Captcha url %s', captcha_url) + + login_form_data['captcha_sid'] = response_url_dict['sid'] + login_form_data['captcha_key'] = self.get_captcha_key(captcha_url) + + response = self.auth_session.post(captcha_form_action, login_form_data) + + # logger.debug('Cookies %s', self.auth_session.cookies) + # if 'remixsid' not in self.auth_session.cookies and 'remixsid6' not in self.auth_session.cookies: + # raise VkAuthError('Authorization error (Bad password or captcha key)') + + def phone_number_is_needed(self, text): + raise VkAuthError('Phone number is needed') + + def get_auth_check_code(self): + raise VkAuthError('Auth check code is needed') + + +class InteractiveMixin(object): + def get_user_login(self): + user_login = raw_input('VK user login: ') + return user_login.strip() + + def get_user_password(self): + import getpass + + user_password = getpass.getpass('VK user password: ') + return user_password + + def get_access_token(self): + logger.debug('InteractiveMixin.get_access_token()') + access_token = super(InteractiveMixin, self).get_access_token() + if not access_token: + access_token = raw_input('VK API access token: ') + return access_token + + def get_captcha_key(self, captcha_image_url): + """ + Read CAPTCHA key from shell + """ + print('Open CAPTCHA image url: ', captcha_image_url) + captcha_key = raw_input('Enter CAPTCHA key: ') + return captcha_key + + def get_auth_check_code(self): + """ + Read Auth code from shell + """ + auth_check_code = raw_input('Auth check code: ') + return auth_check_code.strip() diff --git a/script.module.vk/lib/vk/mixins.pyc b/script.module.vk/lib/vk/mixins.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8fb2e5daf101e05000f2537c1b26d37950ec031 GIT binary patch literal 7644 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 HXMXW-j7k;G literal 0 HcmV?d00001 diff --git a/script.module.vk/lib/vk/tests.py b/script.module.vk/lib/vk/tests.py new file mode 100644 index 0000000..8b4a2ab --- /dev/null +++ b/script.module.vk/lib/vk/tests.py @@ -0,0 +1,60 @@ +# coding=utf8 + +import os +import sys +import time + +import unittest + +import vk +import utils + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + +# copy to test_props.py and fill it +USER_LOGIN = '' # user email or phone number +USER_PASSWORD = '' # user password +APP_ID = '' # aka API/Client ID + +from test_props import USER_LOGIN, USER_PASSWORD, APP_ID + + +class UtilsTestCase(unittest.TestCase): + def test_stringify(self): + self.assertEqual({1: 'str,str2'}, utils.stringify_values({1: ['str', 'str2']})) + + def test_stringify_2(self): + self.assertEqual({1: u'str,стр2'}, utils.stringify_values({1: ['str', u'стр2']})) + + def test_stringify_3(self): + self.assertEqual({1: u'стр,стр2'}, utils.stringify_values({1: [u'стр', u'стр2']})) + + +class VkTestCase(unittest.TestCase): + + def setUp(self): + auth_session = vk.AuthSession(app_id=APP_ID, user_login=USER_LOGIN, user_password=USER_PASSWORD) + access_token, _ = auth_session.get_access_token() + + session = vk.Session(access_token=access_token) + self.vk_api = vk.API(session, lang='ru') + + def test_get_server_time(self): + time_1 = time.time() - 1 + time_2 = time_1 + 10 + server_time = self.vk_api.getServerTime() + self.assertTrue(time_1 <= server_time <= time_2) + + def test_get_server_time_via_token_api(self): + time_1 = time.time() - 1 + time_2 = time_1 + 10 + server_time = self.vk_api.getServerTime() + self.assertTrue(time_1 <= server_time <= time_2) + + def test_get_profiles_via_token(self): + profiles = self.vk_api.users.get(user_id=1) + self.assertEqual(profiles[0]['last_name'], u'Дуров') + + +if __name__ == '__main__': + unittest.main() diff --git a/script.module.vk/lib/vk/tests.pyc b/script.module.vk/lib/vk/tests.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b7d825f5dbb92b1edcba82c3d016055989dbe81 GIT binary patch literal 3048 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