コード例 #1
0
ファイル: application.py プロジェクト: norpadon/vk_async
    def __init__(self, app_id, username, password, max_requests_per_second=3,
                 default_timeout=20, api_version='5.28'):
        """Create new application object

        Args:
            app_id: id of VK application.
            username: user's phone number or email.
            password: user's password.
            max_requests_per_second: maximum number of requests that
                application can send per one second.
                Depends on number of users. Default value is 3.
            default_timeout: default timeout for ip requests.
            api_version: version of VK API used.
        """
        self.last_requests = deque([datetime.min] * max_requests_per_second)
        self.max_requests_per_second = max_requests_per_second

        self.app_id = app_id
        self.username = username
        self.password = password

        self.api_version = api_version

        self.default_timeout = default_timeout

        self.client_session = Session(AsyncHTTPClient)
        self.queue_lock = Lock()

        IOLoop.current().run_sync(self.get_access_token)
コード例 #2
0
import tornado.testing
import tornado.web
from tornado.ioloop import IOLoop

from httpclient_session import Session

session = Session()


class AsyncHTTPTestCase(tornado.testing.AsyncHTTPTestCase):
    def __init__(self, methodName='runTest', **kwargs):
        super(AsyncHTTPTestCase, self).__init__(methodName, **kwargs)

    def get_app(self):
        return app

    def get_new_ioloop(self):
        return IOLoop.current()

    def get_http_client(self):
        return session


class CookieHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_cookie('key'):
            self.set_cookie('key', 'value')
        self.write('ok')


app = tornado.web.Application(
コード例 #3
0
ファイル: application.py プロジェクト: norpadon/vk_async
class Application(object):
    """Wrapper around single vk application."""

    LOGIN_URL = 'https://m.vk.com'
    AUTHORIZE_URL = 'https://oauth.vk.com/authorize'
    CAPTCHA_URI = 'https://m.vk.com/captcha.php'
    API_URL = 'https://api.vk.com/method/'

    SCOPE = 'offline'

    DEFAULT_HEADERS = HTTPHeaders({
        'accept': 'application/json',
        'accept-charset': 'utf-8',
        'content-type': 'application/x-www-form-urlencoded',
    })

    def __init__(self, app_id, username, password, max_requests_per_second=3,
                 default_timeout=20, api_version='5.28'):
        """Create new application object

        Args:
            app_id: id of VK application.
            username: user's phone number or email.
            password: user's password.
            max_requests_per_second: maximum number of requests that
                application can send per one second.
                Depends on number of users. Default value is 3.
            default_timeout: default timeout for ip requests.
            api_version: version of VK API used.
        """
        self.last_requests = deque([datetime.min] * max_requests_per_second)
        self.max_requests_per_second = max_requests_per_second

        self.app_id = app_id
        self.username = username
        self.password = password

        self.api_version = api_version

        self.default_timeout = default_timeout

        self.client_session = Session(AsyncHTTPClient)
        self.queue_lock = Lock()

        IOLoop.current().run_sync(self.get_access_token)

    @gen.coroutine
    def _post(self, url, params, handle_redirects=True):
        """Make HTTP post request, handle redirects and timeouts.

        Args:
            url: url to make request.
            params: request parameters.
            handle_redirects: process redirects manually
                (use to handle cookies properly).
        """
        request = HTTPRequest(
            url,
            method='POST',
            headers=self.DEFAULT_HEADERS.copy(),
            follow_redirects=not handle_redirects,
            request_timeout=self.default_timeout,
            body=urlencode(params),
        )

        # Handle timeouts.
        while True:
            try:
                # If no redirect.
                response = yield self.client_session.fetch(request)
                return response
            except HTTPError as e:
                # If timeout happened just retry.
                if e.code == 599:
                    continue
                # Else it is redirect.
                response = e.response
                break

        # If access token has been acquired.
        if response.code == 405:
            return response

        # Handle redirect.
        new_url = response.headers['Location']
        if new_url == '/':
            new_url = self.LOGIN_URL
        # Save cookies.
        extract_cookies_to_jar(self.client_session.cookies, request, response)

        response = yield self._post(new_url, params, handle_redirects)
        return response

    def __getattr__(self, method_name):
        return APIMethod(self, method_name)

    @gen.coroutine
    def __call__(self, method_name, **method_kwargs):
        response = yield self.method_request(method_name, **method_kwargs)

        # There may be 2 dicts in one JSON.
        # For example: {'error': ...}{'response': ...}.
        errors = []
        error_codes = []
        for data in json_iter_parse(response.body.decode('utf-8')):
            if 'error' in data:
                error_data = data['error']
                error_codes.append(error_data['error_code'])
                errors.append(error_data)

            if 'response' in data:
                for error in errors:
                    logger.warning(str(error))

                return data['response']

        # Handle "Too many requests" error.
        if TO_MANY_REQUESTS in error_codes:
            return (yield self(method_name, **method_kwargs))

        raise VkAPIMethodError(errors[0])

    @gen.coroutine
    def method_request(self, method_name, **method_kwargs):
        """Make call to VK API.

        Args:
            method_name: name of VK API method.
            **method_args: arguments to VK API method.
        """
        # Wait if too many requests were made.
        with (yield self.queue_lock.acquire()):
            first_request = self.last_requests.pop()
            now = datetime.now()
            delay = max(0, 1.1 - (now - first_request).total_seconds())
            yield gen.sleep(delay)

            params = {
                'timestamp': int(time.time()),
                'access_token': self.access_token,
                'v': self.api_version,
            }

            method_kwargs = stringify_values(method_kwargs)
            params.update(method_kwargs)
            url = self.API_URL + method_name

            result = yield self._post(url, params)
            self.last_requests.appendleft(datetime.now())

        return result

    @gen.coroutine
    def get_access_token(self):
        """Get access token using app id and user login and password."""
        # Log in and get cookies.
        yield self.login()
        # Authorize via OAuth2.
        auth_response_url_query = yield self.oauth2_authorization()

        if 'access_token' in auth_response_url_query:
            self.access_token = auth_response_url_query['access_token']
        else:
            raise VkAuthError('OAuth2 authorization error')

    @gen.coroutine
    def login(self, login_form_action=None):
        """Log in and set cookies.

        Args:
            login_form_action: url of login action.
                Default is retrieved from m.vk.api/login.
        """
        if login_form_action is None:
            while True:
                try:
                    response = yield self.client_session.fetch(self.LOGIN_URL)
                    break
                except HTTPError:
                    pass

            login_form_action = get_form_action(response.body.decode('utf-8'))
            if not login_form_action:
                raise VkAuthError('VK changed login flow')

        login_form_data = {
            'email': self.username,
            'pass': self.password,
        }

        response = yield self._post(
            login_form_action,
            login_form_data,
        )

        # Check for session id.
        if ('remixsid' in self.client_session.cookies or
                'remixsid6' in self.client_session.cookies):
            return response

        raise VkAuthError('Authorization error (incorrect password)')

    @gen.coroutine
    def oauth2_authorization(self):
        """OAuth2 procedure for getting access token."""
        auth_data = {
            'client_id': self.app_id,
            'display': 'mobile',
            'response_type': 'token',
            'scope': self.SCOPE,
            'v': self.api_version,
        }

        response = yield self._post(
            self.AUTHORIZE_URL,
            auth_data,
        )

        response_url_query = get_url_query(response.effective_url)
        if 'access_token' in response_url_query:
            return response_url_query

        # Permissions is needed
        response_body = response.body.decode('utf-8')
        form_action = get_form_action(response_body)
        if form_action:
            response = yield self._post(form_action, {})
            response_url_query = get_url_query(response.effective_url)
            return response_url_query

        try:
            response_json = json.loads(response_body)
        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']
            )
        raise VkAuthError(error_message)
コード例 #4
0
import tornado.testing
import tornado.web
from tornado.ioloop import IOLoop

from httpclient_session import Session

session = Session(io_loop=IOLoop.current())


class AsyncHTTPTestCase(tornado.testing.AsyncHTTPTestCase):
    def __init__(self, methodName='runTest', **kwargs):
        super(AsyncHTTPTestCase, self).__init__(methodName, **kwargs)

    def get_app(self):
        return app

    def get_new_ioloop(self):
        return IOLoop.current()

    def get_http_client(self):
        return session


class CookieHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_cookie('key'):
            self.set_cookie('key', 'value')
        self.write('ok')


app = tornado.web.Application(