mirror of
				https://github.com/MCV-Software/TWBlue.git
				synced 2025-11-04 05:47:05 +00:00 
			
		
		
		
	Install Twython from a git repo instead of shipping it in the source code. #273
This commit is contained in:
		@@ -24,3 +24,4 @@ python-vlc
 | 
				
			|||||||
pywin32
 | 
					pywin32
 | 
				
			||||||
certifi
 | 
					certifi
 | 
				
			||||||
backports.functools_lru_cache
 | 
					backports.functools_lru_cache
 | 
				
			||||||
 | 
					git+https://github.com/manuelcortez/twython
 | 
				
			||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
#     ______                  __   __
 | 
					 | 
				
			||||||
#    /_  __/_      __ __  __ / /_ / /_   ____   ____
 | 
					 | 
				
			||||||
#     / /  | | /| / // / / // __// __ \ / __ \ / __ \
 | 
					 | 
				
			||||||
#    / /   | |/ |/ // /_/ // /_ / / / // /_/ // / / /
 | 
					 | 
				
			||||||
#   /_/    |__/|__/ \__, / \__//_/ /_/ \____//_/ /_/
 | 
					 | 
				
			||||||
#                  /____/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
Twython
 | 
					 | 
				
			||||||
-------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Twython is a library for Python that wraps the Twitter API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
It aims to abstract away all the API endpoints, so that
 | 
					 | 
				
			||||||
additions to the library and/or the Twitter API won't
 | 
					 | 
				
			||||||
cause any overall problems.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Questions, comments? ryan@venodesigns.net
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
 | 
					 | 
				
			||||||
__version__ = '3.7.0'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .api import Twython
 | 
					 | 
				
			||||||
from .streaming import TwythonStreamer
 | 
					 | 
				
			||||||
from .exceptions import (
 | 
					 | 
				
			||||||
    TwythonError, TwythonRateLimitError, TwythonAuthError,
 | 
					 | 
				
			||||||
    TwythonStreamError
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
@@ -1,22 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.advisory
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains Warning classes for Twython to specifically
 | 
					 | 
				
			||||||
alert the user about.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This mainly is because Python 2.7 > mutes DeprecationWarning and when
 | 
					 | 
				
			||||||
we deprecate a method or variable in Twython, we want to use to see
 | 
					 | 
				
			||||||
the Warning but don't want ALL DeprecationWarnings to appear,
 | 
					 | 
				
			||||||
only TwythonDeprecationWarnings.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonDeprecationWarning(DeprecationWarning):
 | 
					 | 
				
			||||||
    """Custom DeprecationWarning to be raised when methods/variables
 | 
					 | 
				
			||||||
    are being deprecated in Twython. Python 2.7 > ignores DeprecationWarning
 | 
					 | 
				
			||||||
    so we want to specifcally bubble up ONLY Twython Deprecation Warnings
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
@@ -1,708 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.api
 | 
					 | 
				
			||||||
~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains functionality for access to core Twitter API calls,
 | 
					 | 
				
			||||||
Twitter Authentication, and miscellaneous methods that are useful when
 | 
					 | 
				
			||||||
dealing with the Twitter API
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import warnings
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import requests
 | 
					 | 
				
			||||||
from requests.auth import HTTPBasicAuth
 | 
					 | 
				
			||||||
from requests_oauthlib import OAuth1, OAuth2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from . import __version__
 | 
					 | 
				
			||||||
from .advisory import TwythonDeprecationWarning
 | 
					 | 
				
			||||||
from .compat import json, urlencode, parse_qsl, quote_plus, str, is_py2
 | 
					 | 
				
			||||||
from .compat import urlsplit
 | 
					 | 
				
			||||||
from .endpoints import EndpointsMixin
 | 
					 | 
				
			||||||
from .exceptions import TwythonError, TwythonAuthError, TwythonRateLimitError
 | 
					 | 
				
			||||||
from .helpers import _transparent_params
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
warnings.simplefilter('always', TwythonDeprecationWarning)  # For Python 2.7 >
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Twython(EndpointsMixin, object):
 | 
					 | 
				
			||||||
    def __init__(self, app_key=None, app_secret=None, oauth_token=None,
 | 
					 | 
				
			||||||
                 oauth_token_secret=None, access_token=None,
 | 
					 | 
				
			||||||
                 token_type='bearer', oauth_version=1, api_version='1.1',
 | 
					 | 
				
			||||||
                 client_args=None, auth_endpoint='authenticate'):
 | 
					 | 
				
			||||||
        """Instantiates an instance of Twython. Takes optional parameters for
 | 
					 | 
				
			||||||
        authentication and such (see below).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param app_key: (optional) Your applications key
 | 
					 | 
				
			||||||
        :param app_secret: (optional) Your applications secret key
 | 
					 | 
				
			||||||
        :param oauth_token: (optional) When using **OAuth 1**, combined with
 | 
					 | 
				
			||||||
        oauth_token_secret to make authenticated calls
 | 
					 | 
				
			||||||
        :param oauth_token_secret: (optional) When using **OAuth 1** combined
 | 
					 | 
				
			||||||
        with oauth_token to make authenticated calls
 | 
					 | 
				
			||||||
        :param access_token: (optional) When using **OAuth 2**, provide a
 | 
					 | 
				
			||||||
        valid access token if you have one
 | 
					 | 
				
			||||||
        :param token_type: (optional) When using **OAuth 2**, provide your
 | 
					 | 
				
			||||||
        token type. Default: bearer
 | 
					 | 
				
			||||||
        :param oauth_version: (optional) Choose which OAuth version to use.
 | 
					 | 
				
			||||||
        Default: 1
 | 
					 | 
				
			||||||
        :param api_version: (optional) Choose which Twitter API version to
 | 
					 | 
				
			||||||
        use. Default: 1.1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param client_args: (optional) Accepts some requests Session parameters
 | 
					 | 
				
			||||||
        and some requests Request parameters.
 | 
					 | 
				
			||||||
              See http://docs.python-requests.org/en/latest/api/#sessionapi
 | 
					 | 
				
			||||||
              and requests section below it for details.
 | 
					 | 
				
			||||||
              [ex. headers, proxies, verify(SSL verification)]
 | 
					 | 
				
			||||||
        :param auth_endpoint: (optional) Lets you select which authentication
 | 
					 | 
				
			||||||
        endpoint will use your application.
 | 
					 | 
				
			||||||
              This will allow the application to have DM access
 | 
					 | 
				
			||||||
              if the endpoint is 'authorize'.
 | 
					 | 
				
			||||||
                Default: authenticate.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # API urls, OAuth urls and API version; needed for hitting that there
 | 
					 | 
				
			||||||
        # API.
 | 
					 | 
				
			||||||
        self.api_version = api_version
 | 
					 | 
				
			||||||
        self.api_url = 'https://api.twitter.com/%s'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.app_key = app_key
 | 
					 | 
				
			||||||
        self.app_secret = app_secret
 | 
					 | 
				
			||||||
        self.oauth_token = oauth_token
 | 
					 | 
				
			||||||
        self.oauth_token_secret = oauth_token_secret
 | 
					 | 
				
			||||||
        self.access_token = access_token
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # OAuth 1
 | 
					 | 
				
			||||||
        self.request_token_url = self.api_url % 'oauth/request_token'
 | 
					 | 
				
			||||||
        self.access_token_url = self.api_url % 'oauth/access_token'
 | 
					 | 
				
			||||||
        self.authenticate_url = self.api_url % ('oauth/%s' % auth_endpoint)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.access_token:  # If they pass an access token, force OAuth 2
 | 
					 | 
				
			||||||
            oauth_version = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.oauth_version = oauth_version
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # OAuth 2
 | 
					 | 
				
			||||||
        if oauth_version == 2:
 | 
					 | 
				
			||||||
            self.request_token_url = self.api_url % 'oauth2/token'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client_args = client_args or {}
 | 
					 | 
				
			||||||
        default_headers = {'User-Agent': 'Twython v' + __version__}
 | 
					 | 
				
			||||||
        if 'headers' not in self.client_args:
 | 
					 | 
				
			||||||
            # If they didn't set any headers, set our defaults for them
 | 
					 | 
				
			||||||
            self.client_args['headers'] = default_headers
 | 
					 | 
				
			||||||
        elif 'User-Agent' not in self.client_args['headers']:
 | 
					 | 
				
			||||||
            # If they set headers, but didn't include User-Agent.. set
 | 
					 | 
				
			||||||
            # it for them
 | 
					 | 
				
			||||||
            self.client_args['headers'].update(default_headers)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Generate OAuth authentication object for the request
 | 
					 | 
				
			||||||
        # If no keys/tokens are passed to __init__, auth=None allows for
 | 
					 | 
				
			||||||
        # unauthenticated requests, although I think all v1.1 requests
 | 
					 | 
				
			||||||
        # need auth
 | 
					 | 
				
			||||||
        auth = None
 | 
					 | 
				
			||||||
        if oauth_version == 1:
 | 
					 | 
				
			||||||
            # User Authentication is through OAuth 1
 | 
					 | 
				
			||||||
            if self.app_key is not None and self.app_secret is not None:
 | 
					 | 
				
			||||||
                auth = OAuth1(self.app_key, self.app_secret,
 | 
					 | 
				
			||||||
                                self.oauth_token, self.oauth_token_secret)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        elif oauth_version == 2 and self.access_token:
 | 
					 | 
				
			||||||
            # Application Authentication is through OAuth 2
 | 
					 | 
				
			||||||
            token = {'token_type': token_type,
 | 
					 | 
				
			||||||
                     'access_token': self.access_token}
 | 
					 | 
				
			||||||
            auth = OAuth2(self.app_key, token=token)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client = requests.Session()
 | 
					 | 
				
			||||||
        self.client.auth = auth
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Make a copy of the client args and iterate over them
 | 
					 | 
				
			||||||
        # Pop out all the acceptable args at this point because they will
 | 
					 | 
				
			||||||
        # Never be used again.
 | 
					 | 
				
			||||||
        client_args_copy = self.client_args.copy()
 | 
					 | 
				
			||||||
        for k, v in client_args_copy.items():
 | 
					 | 
				
			||||||
            if k in ('cert', 'hooks', 'max_redirects', 'proxies'):
 | 
					 | 
				
			||||||
                setattr(self.client, k, v)
 | 
					 | 
				
			||||||
                self.client_args.pop(k)  # Pop, pop!
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Headers are always present, so we unconditionally pop them and merge
 | 
					 | 
				
			||||||
        # them into the session headers.
 | 
					 | 
				
			||||||
        self.client.headers.update(self.client_args.pop('headers'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self._last_call = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        return '<Twython: %s>' % (self.app_key)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _request(self, url, method='GET', params=None, api_call=None, json_encoded=False):
 | 
					 | 
				
			||||||
        """Internal request method"""
 | 
					 | 
				
			||||||
        method = method.lower()
 | 
					 | 
				
			||||||
        params = params or {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        func = getattr(self.client, method)
 | 
					 | 
				
			||||||
        if isinstance(params, dict) and json_encoded == False:
 | 
					 | 
				
			||||||
            params, files = _transparent_params(params)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            params = params
 | 
					 | 
				
			||||||
            files = list()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        requests_args = {}
 | 
					 | 
				
			||||||
        for k, v in self.client_args.items():
 | 
					 | 
				
			||||||
            # Maybe this should be set as a class variable and only done once?
 | 
					 | 
				
			||||||
            if k in ('timeout', 'allow_redirects', 'stream', 'verify'):
 | 
					 | 
				
			||||||
                requests_args[k] = v
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if method == 'get' or method == 'delete':
 | 
					 | 
				
			||||||
            requests_args['params'] = params
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # Check for json_encoded so we will sent params as "data" or "json"
 | 
					 | 
				
			||||||
            if json_encoded:
 | 
					 | 
				
			||||||
                data_key = "json"
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
               data_key = "data"
 | 
					 | 
				
			||||||
            requests_args.update({
 | 
					 | 
				
			||||||
                data_key: params,
 | 
					 | 
				
			||||||
                'files': files,
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            response = func(url, **requests_args)
 | 
					 | 
				
			||||||
        except requests.RequestException as e:
 | 
					 | 
				
			||||||
            raise TwythonError(str(e))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # create stash for last function intel
 | 
					 | 
				
			||||||
        self._last_call = {
 | 
					 | 
				
			||||||
            'api_call': api_call,
 | 
					 | 
				
			||||||
            'api_error': None,
 | 
					 | 
				
			||||||
            'cookies': response.cookies,
 | 
					 | 
				
			||||||
            'headers': response.headers,
 | 
					 | 
				
			||||||
            'status_code': response.status_code,
 | 
					 | 
				
			||||||
            'url': response.url,
 | 
					 | 
				
			||||||
            'content': response.text,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # greater than 304 (not modified) is an error
 | 
					 | 
				
			||||||
        if response.status_code > 304:
 | 
					 | 
				
			||||||
            error_message = self._get_error_message(response)
 | 
					 | 
				
			||||||
            self._last_call['api_error'] = error_message
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ExceptionType = TwythonError
 | 
					 | 
				
			||||||
            if response.status_code == 429:
 | 
					 | 
				
			||||||
                # Twitter API 1.1, always return 429 when
 | 
					 | 
				
			||||||
                # rate limit is exceeded
 | 
					 | 
				
			||||||
                ExceptionType = TwythonRateLimitError
 | 
					 | 
				
			||||||
            elif response.status_code == 401 or 'Bad Authentication data' \
 | 
					 | 
				
			||||||
                    in error_message:
 | 
					 | 
				
			||||||
                # Twitter API 1.1, returns a 401 Unauthorized or
 | 
					 | 
				
			||||||
                # a 400 "Bad Authentication data" for invalid/expired
 | 
					 | 
				
			||||||
                # app keys/user tokens
 | 
					 | 
				
			||||||
                ExceptionType = TwythonAuthError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            raise ExceptionType(
 | 
					 | 
				
			||||||
                error_message,
 | 
					 | 
				
			||||||
                error_code=response.status_code,
 | 
					 | 
				
			||||||
                retry_after=response.headers.get('X-Rate-Limit-Reset'))
 | 
					 | 
				
			||||||
        content = ''
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            if response.status_code == 204:
 | 
					 | 
				
			||||||
                content = response.content
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                content = response.json()
 | 
					 | 
				
			||||||
        except ValueError:
 | 
					 | 
				
			||||||
            if response.content != '':
 | 
					 | 
				
			||||||
                raise TwythonError('Response was not valid JSON. \
 | 
					 | 
				
			||||||
                                   Unable to decode.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return content
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _get_error_message(self, response):
 | 
					 | 
				
			||||||
        """Parse and return the first error message"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        error_message = 'An error occurred processing your request.'
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            content = response.json()
 | 
					 | 
				
			||||||
            # {"errors":[{"code":34,"message":"Sorry,
 | 
					 | 
				
			||||||
            # that page does not exist"}]}
 | 
					 | 
				
			||||||
            error_message = content['errors'][0]['message']
 | 
					 | 
				
			||||||
        except TypeError:
 | 
					 | 
				
			||||||
            error_message = content['errors']
 | 
					 | 
				
			||||||
        except ValueError:
 | 
					 | 
				
			||||||
            # bad json data from Twitter for an error
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        except (KeyError, IndexError):
 | 
					 | 
				
			||||||
            # missing data so fallback to default message
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return error_message
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def request(self, endpoint, method='GET', params=None, version='1.1', json_encoded=False):
 | 
					 | 
				
			||||||
        """Return dict of response received from Twitter's API
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param endpoint: (required) Full url or Twitter API endpoint
 | 
					 | 
				
			||||||
                         (e.g. search/tweets)
 | 
					 | 
				
			||||||
        :type endpoint: string
 | 
					 | 
				
			||||||
        :param method: (optional) Method of accessing data, either
 | 
					 | 
				
			||||||
                       GET, POST or DELETE. (default GET)
 | 
					 | 
				
			||||||
        :type method: string
 | 
					 | 
				
			||||||
        :param params: (optional) Dict of parameters (if any) accepted
 | 
					 | 
				
			||||||
                       the by Twitter API endpoint you are trying to
 | 
					 | 
				
			||||||
                       access (default None)
 | 
					 | 
				
			||||||
        :type params: dict or None
 | 
					 | 
				
			||||||
        :param version: (optional) Twitter API version to access
 | 
					 | 
				
			||||||
                        (default 1.1)
 | 
					 | 
				
			||||||
        :type version: string
 | 
					 | 
				
			||||||
        :param json_encoded: (optional) Flag to indicate if this method should send data encoded as json
 | 
					 | 
				
			||||||
                        (default False)
 | 
					 | 
				
			||||||
        :type json_encoded: bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :rtype: dict
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if endpoint.startswith('http://'):
 | 
					 | 
				
			||||||
            raise TwythonError('api.twitter.com is restricted to SSL/TLS traffic.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # In case they want to pass a full Twitter URL
 | 
					 | 
				
			||||||
        # i.e. https://api.twitter.com/1.1/search/tweets.json
 | 
					 | 
				
			||||||
        if endpoint.startswith('https://'):
 | 
					 | 
				
			||||||
            url = endpoint
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            url = '%s/%s.json' % (self.api_url % version, endpoint)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        content = self._request(url, method=method, params=params,
 | 
					 | 
				
			||||||
                                api_call=url, json_encoded=json_encoded)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return content
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get(self, endpoint, params=None, version='1.1'):
 | 
					 | 
				
			||||||
        """Shortcut for GET requests via :class:`request`"""
 | 
					 | 
				
			||||||
        return self.request(endpoint, params=params, version=version)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def post(self, endpoint, params=None, version='1.1', json_encoded=False):
 | 
					 | 
				
			||||||
        """Shortcut for POST requests via :class:`request`"""
 | 
					 | 
				
			||||||
        return self.request(endpoint, 'POST', params=params, version=version, json_encoded=json_encoded)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def delete(self, endpoint, params=None, version='1.1', json_encoded=False):
 | 
					 | 
				
			||||||
        """Shortcut for delete requests via :class:`request`"""
 | 
					 | 
				
			||||||
        return self.request(endpoint, 'DELETE', params=params, version=version, json_encoded=json_encoded)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_lastfunction_header(self, header, default_return_value=None):
 | 
					 | 
				
			||||||
        """Returns a specific header from the last API call
 | 
					 | 
				
			||||||
        This will return None if the header is not present
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param header: (required) The name of the header you want to get
 | 
					 | 
				
			||||||
                       the value of
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Most useful for the following header information:
 | 
					 | 
				
			||||||
            x-rate-limit-limit,
 | 
					 | 
				
			||||||
            x-rate-limit-remaining,
 | 
					 | 
				
			||||||
            x-rate-limit-class,
 | 
					 | 
				
			||||||
            x-rate-limit-reset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self._last_call is None:
 | 
					 | 
				
			||||||
            raise TwythonError('This function must be called after an API call. \
 | 
					 | 
				
			||||||
                               It delivers header information.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self._last_call['headers'].get(header, default_return_value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_authentication_tokens(self, callback_url=None, force_login=False,
 | 
					 | 
				
			||||||
                                  screen_name=''):
 | 
					 | 
				
			||||||
        """Returns a dict including an authorization URL, ``auth_url``, to
 | 
					 | 
				
			||||||
           direct a user to
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param callback_url: (optional) Url the user is returned to after
 | 
					 | 
				
			||||||
                             they authorize your app (web clients only)
 | 
					 | 
				
			||||||
        :param force_login: (optional) Forces the user to enter their
 | 
					 | 
				
			||||||
                            credentials to ensure the correct users
 | 
					 | 
				
			||||||
                            account is authorized.
 | 
					 | 
				
			||||||
        :param screen_name: (optional) If forced_login is set OR user is
 | 
					 | 
				
			||||||
                            not currently logged in, Prefills the username
 | 
					 | 
				
			||||||
                            input box of the OAuth login screen with the
 | 
					 | 
				
			||||||
                            given value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :rtype: dict
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.oauth_version != 1:
 | 
					 | 
				
			||||||
            raise TwythonError('This method can only be called when your \
 | 
					 | 
				
			||||||
                               OAuth version is 1.0.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request_args = {}
 | 
					 | 
				
			||||||
        if callback_url:
 | 
					 | 
				
			||||||
            request_args['oauth_callback'] = callback_url
 | 
					 | 
				
			||||||
        response = self.client.get(self.request_token_url, params=request_args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if response.status_code == 401:
 | 
					 | 
				
			||||||
            raise TwythonAuthError(response.content,
 | 
					 | 
				
			||||||
                                   error_code=response.status_code)
 | 
					 | 
				
			||||||
        elif response.status_code != 200:
 | 
					 | 
				
			||||||
            raise TwythonError(response.content,
 | 
					 | 
				
			||||||
                               error_code=response.status_code)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request_tokens = dict(parse_qsl(response.content.decode('utf-8')))
 | 
					 | 
				
			||||||
        if not request_tokens:
 | 
					 | 
				
			||||||
            raise TwythonError('Unable to decode request tokens.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        oauth_callback_confirmed = request_tokens.get('oauth_callback_confirmed') \
 | 
					 | 
				
			||||||
            == 'true'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auth_url_params = {
 | 
					 | 
				
			||||||
            'oauth_token': request_tokens['oauth_token'],
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if force_login:
 | 
					 | 
				
			||||||
            auth_url_params.update({
 | 
					 | 
				
			||||||
                'force_login': force_login,
 | 
					 | 
				
			||||||
                'screen_name': screen_name
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Use old-style callback argument if server didn't accept new-style
 | 
					 | 
				
			||||||
        if callback_url and not oauth_callback_confirmed:
 | 
					 | 
				
			||||||
            auth_url_params['oauth_callback'] = self.callback_url
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        request_tokens['auth_url'] = self.authenticate_url + \
 | 
					 | 
				
			||||||
            '?' + urlencode(auth_url_params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return request_tokens
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_authorized_tokens(self, oauth_verifier):
 | 
					 | 
				
			||||||
        """Returns a dict of authorized tokens after they go through the
 | 
					 | 
				
			||||||
        :class:`get_authentication_tokens` phase.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param oauth_verifier: (required) The oauth_verifier (or a.k.a PIN
 | 
					 | 
				
			||||||
        for non web apps) retrieved from the callback url querystring
 | 
					 | 
				
			||||||
        :rtype: dict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.oauth_version != 1:
 | 
					 | 
				
			||||||
            raise TwythonError('This method can only be called when your \
 | 
					 | 
				
			||||||
                               OAuth version is 1.0.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        response = self.client.get(self.access_token_url,
 | 
					 | 
				
			||||||
                                   params={'oauth_verifier': oauth_verifier},
 | 
					 | 
				
			||||||
                                   headers={'Content-Type': 'application/\
 | 
					 | 
				
			||||||
                                   json'})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if response.status_code == 401:
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    # try to get json
 | 
					 | 
				
			||||||
                    content = response.json()
 | 
					 | 
				
			||||||
                except AttributeError:  # pragma: no cover
 | 
					 | 
				
			||||||
                    # if unicode detected
 | 
					 | 
				
			||||||
                    content = json.loads(response.content)
 | 
					 | 
				
			||||||
            except ValueError:
 | 
					 | 
				
			||||||
                content = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            raise TwythonError(content.get('error', 'Invalid / expired To \
 | 
					 | 
				
			||||||
            ken'), error_code=response.status_code)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        authorized_tokens = dict(parse_qsl(response.content.decode('utf-8')))
 | 
					 | 
				
			||||||
        if not authorized_tokens:
 | 
					 | 
				
			||||||
            raise TwythonError('Unable to decode authorized tokens.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return authorized_tokens  # pragma: no cover
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def obtain_access_token(self):
 | 
					 | 
				
			||||||
        """Returns an OAuth 2 access token to make OAuth 2 authenticated
 | 
					 | 
				
			||||||
        read-only calls.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :rtype: string
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.oauth_version != 2:
 | 
					 | 
				
			||||||
            raise TwythonError('This method can only be called when your \
 | 
					 | 
				
			||||||
                               OAuth version is 2.0.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        data = {'grant_type': 'client_credentials'}
 | 
					 | 
				
			||||||
        basic_auth = HTTPBasicAuth(self.app_key, self.app_secret)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            response = self.client.post(self.request_token_url,
 | 
					 | 
				
			||||||
                                        data=data, auth=basic_auth)
 | 
					 | 
				
			||||||
            content = response.content.decode('utf-8')
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                content = content.json()
 | 
					 | 
				
			||||||
            except AttributeError:
 | 
					 | 
				
			||||||
                content = json.loads(content)
 | 
					 | 
				
			||||||
                access_token = content['access_token']
 | 
					 | 
				
			||||||
        except (KeyError, ValueError, requests.exceptions.RequestException):
 | 
					 | 
				
			||||||
            raise TwythonAuthError('Unable to obtain OAuth 2 access token.')
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            return access_token
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def construct_api_url(api_url, **params):
 | 
					 | 
				
			||||||
        """Construct a Twitter API url, encoded, with parameters
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param api_url: URL of the Twitter API endpoint you are attempting
 | 
					 | 
				
			||||||
        to construct
 | 
					 | 
				
			||||||
        :param \*\*params: Parameters that are accepted by Twitter for the
 | 
					 | 
				
			||||||
        endpoint you're requesting
 | 
					 | 
				
			||||||
        :rtype: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Usage::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          >>> from twython import Twython
 | 
					 | 
				
			||||||
          >>> twitter = Twython()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          >>> api_url = 'https://api.twitter.com/1.1/search/tweets.json'
 | 
					 | 
				
			||||||
          >>> constructed_url = twitter.construct_api_url(api_url, q='python',
 | 
					 | 
				
			||||||
          result_type='popular')
 | 
					 | 
				
			||||||
          >>> print constructed_url
 | 
					 | 
				
			||||||
          https://api.twitter.com/1.1/search/tweets.json?q=python&result_type=popular
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        querystring = []
 | 
					 | 
				
			||||||
        params, _ = _transparent_params(params or {})
 | 
					 | 
				
			||||||
        params = requests.utils.to_key_val_list(params)
 | 
					 | 
				
			||||||
        for (k, v) in params:
 | 
					 | 
				
			||||||
            querystring.append(
 | 
					 | 
				
			||||||
                '%s=%s' % (Twython.encode(k), quote_plus(Twython.encode(v)))
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        return '%s?%s' % (api_url, '&'.join(querystring))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def search_gen(self, search_query, **params):  # pragma: no cover
 | 
					 | 
				
			||||||
        warnings.warn(
 | 
					 | 
				
			||||||
            'This method is deprecated. You should use Twython.cursor instead. \
 | 
					 | 
				
			||||||
            [eg. Twython.cursor(Twython.search, q=\'your_query\')]',
 | 
					 | 
				
			||||||
            TwythonDeprecationWarning,
 | 
					 | 
				
			||||||
            stacklevel=2
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return self.cursor(self.search, q=search_query, **params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def cursor(self, function, return_pages=False, **params):
 | 
					 | 
				
			||||||
        """Returns a generator for results that match a specified query.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param function: Instance of a Twython function
 | 
					 | 
				
			||||||
        (Twython.get_home_timeline, Twython.search)
 | 
					 | 
				
			||||||
        :param \*\*params: Extra parameters to send with your request
 | 
					 | 
				
			||||||
        (usually parameters accepted by the Twitter API endpoint)
 | 
					 | 
				
			||||||
        :rtype: generator
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Usage::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          >>> from twython import Twython
 | 
					 | 
				
			||||||
          >>> twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN,
 | 
					 | 
				
			||||||
          OAUTH_TOKEN_SECRET)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          >>> results = twitter.cursor(twitter.search, q='python')
 | 
					 | 
				
			||||||
          >>> for result in results:
 | 
					 | 
				
			||||||
          >>>   print result
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if not callable(function):
 | 
					 | 
				
			||||||
            raise TypeError('.cursor() takes a Twython function as its first \
 | 
					 | 
				
			||||||
                            argument. Did you provide the result of a \
 | 
					 | 
				
			||||||
                            function call?')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not hasattr(function, 'iter_mode'):
 | 
					 | 
				
			||||||
            raise TwythonError('Unable to create generator for Twython \
 | 
					 | 
				
			||||||
                               method "%s"' % function.__name__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while True:
 | 
					 | 
				
			||||||
            content = function(**params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if not content:
 | 
					 | 
				
			||||||
                raise StopIteration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if hasattr(function, 'iter_key'):
 | 
					 | 
				
			||||||
                results = content.get(function.iter_key)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                results = content
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if return_pages:
 | 
					 | 
				
			||||||
                yield results
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                for result in results:
 | 
					 | 
				
			||||||
                    yield result
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if function.iter_mode == 'cursor' and \
 | 
					 | 
				
			||||||
               content['next_cursor_str'] == '0':
 | 
					 | 
				
			||||||
                raise StopIteration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                if function.iter_mode == 'id':
 | 
					 | 
				
			||||||
                    # Set max_id in params to one less than lowest tweet id
 | 
					 | 
				
			||||||
                    if hasattr(function, 'iter_metadata'):
 | 
					 | 
				
			||||||
                        # Get supplied next max_id
 | 
					 | 
				
			||||||
                        metadata = content.get(function.iter_metadata)
 | 
					 | 
				
			||||||
                        if 'next_results' in metadata:
 | 
					 | 
				
			||||||
                            next_results = urlsplit(metadata['next_results'])
 | 
					 | 
				
			||||||
                            params = dict(parse_qsl(next_results.query))
 | 
					 | 
				
			||||||
                        else:
 | 
					 | 
				
			||||||
                            # No more results
 | 
					 | 
				
			||||||
                            raise StopIteration
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        # Twitter gives tweets in reverse chronological order:
 | 
					 | 
				
			||||||
                        params['max_id'] = str(int(content[-1]['id_str']) - 1)
 | 
					 | 
				
			||||||
                elif function.iter_mode == 'cursor':
 | 
					 | 
				
			||||||
                    params['cursor'] = content['next_cursor_str']
 | 
					 | 
				
			||||||
            except (TypeError, ValueError):  # pragma: no cover
 | 
					 | 
				
			||||||
                raise TwythonError('Unable to generate next page of search \
 | 
					 | 
				
			||||||
                                   results, `page` is not a number.')
 | 
					 | 
				
			||||||
            except (KeyError, AttributeError):  #pragma no cover
 | 
					 | 
				
			||||||
                raise TwythonError('Unable to generate next page of search \
 | 
					 | 
				
			||||||
                                   results, content has unexpected structure.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def unicode2utf8(text):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            if is_py2 and isinstance(text, str):
 | 
					 | 
				
			||||||
                text = text.encode('utf-8')
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        return text
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def encode(text):
 | 
					 | 
				
			||||||
        if is_py2 and isinstance(text, (str)):
 | 
					 | 
				
			||||||
            return Twython.unicode2utf8(text)
 | 
					 | 
				
			||||||
        return str(text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def html_for_tweet(tweet, use_display_url=True, use_expanded_url=False, expand_quoted_status=False):
 | 
					 | 
				
			||||||
        """Return HTML for a tweet (urls, mentions, hashtags, symbols replaced with links)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param tweet: Tweet object from received from Twitter API
 | 
					 | 
				
			||||||
        :param use_display_url: Use display URL to represent link
 | 
					 | 
				
			||||||
        (ex. google.com, github.com). Default: True
 | 
					 | 
				
			||||||
        :param use_expanded_url: Use expanded URL to represent link
 | 
					 | 
				
			||||||
        (e.g. http://google.com). Default False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        If use_expanded_url is True, it overrides use_display_url.
 | 
					 | 
				
			||||||
        If use_display_url and use_expanded_url is False, short url will
 | 
					 | 
				
			||||||
        be used (t.co/xxxxx)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if 'retweeted_status' in tweet:
 | 
					 | 
				
			||||||
            tweet = tweet['retweeted_status']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if 'extended_tweet' in tweet:
 | 
					 | 
				
			||||||
            tweet = tweet['extended_tweet']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        orig_tweet_text = tweet.get('full_text') or tweet['text']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        display_text_range = tweet.get('display_text_range') or [0, len(orig_tweet_text)]
 | 
					 | 
				
			||||||
        display_text_start, display_text_end = display_text_range[0], display_text_range[1]
 | 
					 | 
				
			||||||
        display_text = orig_tweet_text[display_text_start:display_text_end]
 | 
					 | 
				
			||||||
        prefix_text = orig_tweet_text[0:display_text_start]
 | 
					 | 
				
			||||||
        suffix_text = orig_tweet_text[display_text_end:len(orig_tweet_text)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if 'entities' in tweet:
 | 
					 | 
				
			||||||
            # We'll put all the bits of replacement HTML and their starts/ends
 | 
					 | 
				
			||||||
            # in this list:
 | 
					 | 
				
			||||||
            entities = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Mentions
 | 
					 | 
				
			||||||
            if 'user_mentions' in tweet['entities']:
 | 
					 | 
				
			||||||
                for entity in tweet['entities']['user_mentions']:
 | 
					 | 
				
			||||||
                    temp = {}
 | 
					 | 
				
			||||||
                    temp['start'] = entity['indices'][0]
 | 
					 | 
				
			||||||
                    temp['end'] = entity['indices'][1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    mention_html = '<a href="https://twitter.com/%(screen_name)s" class="twython-mention">@%(screen_name)s</a>' % {'screen_name': entity['screen_name']}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if display_text_start <= temp['start'] <= display_text_end:
 | 
					 | 
				
			||||||
                        temp['replacement'] = mention_html
 | 
					 | 
				
			||||||
                        temp['start'] -= display_text_start
 | 
					 | 
				
			||||||
                        temp['end'] -= display_text_start
 | 
					 | 
				
			||||||
                        entities.append(temp)
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        # Make the '@username' at the start, before
 | 
					 | 
				
			||||||
                        # display_text, into a link:
 | 
					 | 
				
			||||||
                        sub_expr = r'(?<!>)' + orig_tweet_text[temp['start']:temp['end']] + '(?!</a>)'
 | 
					 | 
				
			||||||
                        prefix_text = re.sub(sub_expr, mention_html, prefix_text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Hashtags
 | 
					 | 
				
			||||||
            if 'hashtags' in tweet['entities']:
 | 
					 | 
				
			||||||
                for entity in tweet['entities']['hashtags']:
 | 
					 | 
				
			||||||
                    temp = {}
 | 
					 | 
				
			||||||
                    temp['start'] = entity['indices'][0] - display_text_start
 | 
					 | 
				
			||||||
                    temp['end'] = entity['indices'][1] - display_text_start
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    url_html = '<a href="https://twitter.com/search?q=%%23%(hashtag)s" class="twython-hashtag">#%(hashtag)s</a>' % {'hashtag': entity['text']}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    temp['replacement'] = url_html
 | 
					 | 
				
			||||||
                    entities.append(temp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Symbols
 | 
					 | 
				
			||||||
            if 'symbols' in tweet['entities']:
 | 
					 | 
				
			||||||
                for entity in tweet['entities']['symbols']:
 | 
					 | 
				
			||||||
                    temp = {}
 | 
					 | 
				
			||||||
                    temp['start'] = entity['indices'][0] - display_text_start
 | 
					 | 
				
			||||||
                    temp['end'] = entity['indices'][1] - display_text_start
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    url_html = '<a href="https://twitter.com/search?q=%%24%(symbol)s" class="twython-symbol">$%(symbol)s</a>' % {'symbol': entity['text']}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    temp['replacement'] = url_html
 | 
					 | 
				
			||||||
                    entities.append(temp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # URLs
 | 
					 | 
				
			||||||
            if 'urls' in tweet['entities']:
 | 
					 | 
				
			||||||
                for entity in tweet['entities']['urls']:
 | 
					 | 
				
			||||||
                    temp = {}
 | 
					 | 
				
			||||||
                    temp['start'] = entity['indices'][0] - display_text_start
 | 
					 | 
				
			||||||
                    temp['end'] = entity['indices'][1] - display_text_start
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if use_display_url and entity.get('display_url') and not use_expanded_url:
 | 
					 | 
				
			||||||
                        shown_url = entity['display_url']
 | 
					 | 
				
			||||||
                    elif use_expanded_url and entity.get('expanded_url'):
 | 
					 | 
				
			||||||
                        shown_url = entity['expanded_url']
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        shown_url = entity['url']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    url_html = '<a href="%s" class="twython-url">%s</a>' % (entity['url'], shown_url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if display_text_start <= temp['start'] <= display_text_end:
 | 
					 | 
				
			||||||
                        temp['replacement'] = url_html
 | 
					 | 
				
			||||||
                        entities.append(temp)
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        suffix_text = suffix_text.replace(orig_tweet_text[temp['start']:temp['end']], url_html)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if 'media' in tweet['entities'] and len(tweet['entities']['media']) > 0:
 | 
					 | 
				
			||||||
                # We just link to the overall URL for the tweet's media,
 | 
					 | 
				
			||||||
                # rather than to each individual item.
 | 
					 | 
				
			||||||
                # So, we get the URL from the first media item:
 | 
					 | 
				
			||||||
                entity = tweet['entities']['media'][0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                temp = {}
 | 
					 | 
				
			||||||
                temp['start'] = entity['indices'][0]
 | 
					 | 
				
			||||||
                temp['end'] = entity['indices'][1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if use_display_url and entity.get('display_url') and not use_expanded_url:
 | 
					 | 
				
			||||||
                    shown_url = entity['display_url']
 | 
					 | 
				
			||||||
                elif use_expanded_url and entity.get('expanded_url'):
 | 
					 | 
				
			||||||
                    shown_url = entity['expanded_url']
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    shown_url = entity['url']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                url_html = '<a href="%s" class="twython-media">%s</a>' % (entity['url'], shown_url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if display_text_start <= temp['start'] <= display_text_end:
 | 
					 | 
				
			||||||
                    temp['replacement'] = url_html
 | 
					 | 
				
			||||||
                    entities.append(temp)
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    suffix_text = suffix_text.replace(orig_tweet_text[temp['start']:temp['end']], url_html)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Now do all the replacements, starting from the end, so that the
 | 
					 | 
				
			||||||
            # start/end indices still work:
 | 
					 | 
				
			||||||
            for entity in sorted(entities, key=lambda e: e['start'], reverse=True):
 | 
					 | 
				
			||||||
                display_text = display_text[0:entity['start']] + entity['replacement'] + display_text[entity['end']:]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        quote_text = ''
 | 
					 | 
				
			||||||
        if expand_quoted_status and tweet.get('is_quote_status') and tweet.get('quoted_status'):
 | 
					 | 
				
			||||||
            quoted_status = tweet['quoted_status']
 | 
					 | 
				
			||||||
            quote_text += '<blockquote class="twython-quote">%(quote)s<cite><a href="%(quote_tweet_link)s">' \
 | 
					 | 
				
			||||||
                    '<span class="twython-quote-user-name">%(quote_user_name)s</span>' \
 | 
					 | 
				
			||||||
                    '<span class="twython-quote-user-screenname">@%(quote_user_screen_name)s</span></a>' \
 | 
					 | 
				
			||||||
                    '</cite></blockquote>' % \
 | 
					 | 
				
			||||||
                    {'quote': Twython.html_for_tweet(quoted_status, use_display_url, use_expanded_url, False),
 | 
					 | 
				
			||||||
                     'quote_tweet_link': 'https://twitter.com/%s/status/%s' %
 | 
					 | 
				
			||||||
                                         (quoted_status['user']['screen_name'], quoted_status['id_str']),
 | 
					 | 
				
			||||||
                     'quote_user_name': quoted_status['user']['name'],
 | 
					 | 
				
			||||||
                     'quote_user_screen_name': quoted_status['user']['screen_name']}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return '%(prefix)s%(display)s%(suffix)s%(quote)s' % {
 | 
					 | 
				
			||||||
            'prefix': '<span class="twython-tweet-prefix">%s</span>' % prefix_text if prefix_text else '',
 | 
					 | 
				
			||||||
            'display': display_text,
 | 
					 | 
				
			||||||
            'suffix': '<span class="twython-tweet-suffix">%s</span>' % suffix_text if suffix_text else '',
 | 
					 | 
				
			||||||
            'quote': quote_text
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
@@ -1,40 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.compat
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains imports and declarations for seamless Python 2 and
 | 
					 | 
				
			||||||
Python 3 compatibility.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_ver = sys.version_info
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: Python 2.x?
 | 
					 | 
				
			||||||
is_py2 = (_ver[0] == 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: Python 3.x?
 | 
					 | 
				
			||||||
is_py3 = (_ver[0] == 3)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    import simplejson as json
 | 
					 | 
				
			||||||
except ImportError:
 | 
					 | 
				
			||||||
    import json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if is_py2:
 | 
					 | 
				
			||||||
    from urllib import urlencode, quote_plus
 | 
					 | 
				
			||||||
    from urlparse import parse_qsl, urlsplit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    str = unicode
 | 
					 | 
				
			||||||
    basestring = basestring
 | 
					 | 
				
			||||||
    numeric_types = (int, long, float)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
elif is_py3:
 | 
					 | 
				
			||||||
    from urllib.parse import urlencode, quote_plus, parse_qsl, urlsplit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    str = str
 | 
					 | 
				
			||||||
    basestring = (str, bytes)
 | 
					 | 
				
			||||||
    numeric_types = (int, float)
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,61 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.exceptions
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains Twython specific Exception classes.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .endpoints import TWITTER_HTTP_STATUS_CODE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonError(Exception):
 | 
					 | 
				
			||||||
    """Generic error class, catch-all for most Twython issues.
 | 
					 | 
				
			||||||
    Special cases are handled by TwythonAuthError & TwythonRateLimitError.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from twython import TwythonError, TwythonRateLimitError, TwythonAuthError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self, msg, error_code=None, retry_after=None):
 | 
					 | 
				
			||||||
        self.error_code = error_code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if error_code is not None and error_code in TWITTER_HTTP_STATUS_CODE:
 | 
					 | 
				
			||||||
            msg = 'Twitter API returned a %s (%s), %s' % \
 | 
					 | 
				
			||||||
                  (error_code,
 | 
					 | 
				
			||||||
                   TWITTER_HTTP_STATUS_CODE[error_code][0],
 | 
					 | 
				
			||||||
                   msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        super(TwythonError, self).__init__(msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def msg(self):  # pragma: no cover
 | 
					 | 
				
			||||||
        return self.args[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonAuthError(TwythonError):
 | 
					 | 
				
			||||||
    """Raised when you try to access a protected resource and it fails due to
 | 
					 | 
				
			||||||
    some issue with your authentication.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonRateLimitError(TwythonError):  # pragma: no cover
 | 
					 | 
				
			||||||
    """Raised when you've hit a rate limit.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The amount of seconds to retry your request in will be appended
 | 
					 | 
				
			||||||
    to the message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self, msg, error_code, retry_after=None):
 | 
					 | 
				
			||||||
        if isinstance(retry_after, int):
 | 
					 | 
				
			||||||
            msg = '%s (Retry after %d seconds)' % (msg, retry_after)
 | 
					 | 
				
			||||||
        TwythonError.__init__(self, msg, error_code=error_code)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.retry_after = retry_after
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonStreamError(TwythonError):
 | 
					 | 
				
			||||||
    """Raised when an invalid response from the Stream API is received"""
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
@@ -1,34 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.helpers
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains functions that are repeatedly used throughout
 | 
					 | 
				
			||||||
the Twython library.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .compat import basestring, numeric_types
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _transparent_params(_params):
 | 
					 | 
				
			||||||
    params = {}
 | 
					 | 
				
			||||||
    files = {}
 | 
					 | 
				
			||||||
    for k, v in _params.items():
 | 
					 | 
				
			||||||
        if hasattr(v, 'read') and callable(v.read):
 | 
					 | 
				
			||||||
            files[k] = v  # pragma: no cover
 | 
					 | 
				
			||||||
        elif isinstance(v, bool):
 | 
					 | 
				
			||||||
            if v:
 | 
					 | 
				
			||||||
                params[k] = 'true'
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                params[k] = 'false'
 | 
					 | 
				
			||||||
        elif isinstance(v, basestring) or isinstance(v, numeric_types):
 | 
					 | 
				
			||||||
            params[k] = v
 | 
					 | 
				
			||||||
        elif isinstance(v, list):
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                params[k] = ','.join(v)
 | 
					 | 
				
			||||||
            except TypeError:
 | 
					 | 
				
			||||||
                params[k] = ','.join(map(str, v))
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            continue  # pragma: no cover
 | 
					 | 
				
			||||||
    return params, files
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
from .api import TwythonStreamer
 | 
					 | 
				
			||||||
@@ -1,201 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.streaming.api
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains functionality for interfacing with streaming
 | 
					 | 
				
			||||||
Twitter API calls.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .. import __version__
 | 
					 | 
				
			||||||
from ..compat import json, is_py3
 | 
					 | 
				
			||||||
from ..helpers import _transparent_params
 | 
					 | 
				
			||||||
from .types import TwythonStreamerTypes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import requests
 | 
					 | 
				
			||||||
from requests_oauthlib import OAuth1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonStreamer(object):
 | 
					 | 
				
			||||||
    def __init__(self, app_key, app_secret, oauth_token, oauth_token_secret,
 | 
					 | 
				
			||||||
                 timeout=300, retry_count=None, retry_in=10, client_args=None,
 | 
					 | 
				
			||||||
                 handlers=None, chunk_size=1):
 | 
					 | 
				
			||||||
        """Streaming class for a friendly streaming user experience
 | 
					 | 
				
			||||||
        Authentication IS required to use the Twitter Streaming API
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param app_key: (required) Your applications key
 | 
					 | 
				
			||||||
        :param app_secret: (required) Your applications secret key
 | 
					 | 
				
			||||||
        :param oauth_token: (required) Used with oauth_token_secret to make
 | 
					 | 
				
			||||||
                            authenticated calls
 | 
					 | 
				
			||||||
        :param oauth_token_secret: (required) Used with oauth_token to make
 | 
					 | 
				
			||||||
                                   authenticated calls
 | 
					 | 
				
			||||||
        :param timeout: (optional) How long (in secs) the streamer should wait
 | 
					 | 
				
			||||||
                        for a response from Twitter Streaming API
 | 
					 | 
				
			||||||
        :param retry_count: (optional) Number of times the API call should be
 | 
					 | 
				
			||||||
                            retired
 | 
					 | 
				
			||||||
        :param retry_in: (optional) Amount of time (in secs) the previous
 | 
					 | 
				
			||||||
                         API call should be tried again
 | 
					 | 
				
			||||||
        :param client_args: (optional) Accepts some requests Session
 | 
					 | 
				
			||||||
                            parameters and some requests Request parameters.
 | 
					 | 
				
			||||||
                            See
 | 
					 | 
				
			||||||
                            http://docs.python-requests.org/en/latest/api/#sessionapi
 | 
					 | 
				
			||||||
                            and requests section below it for details.
 | 
					 | 
				
			||||||
                            [ex. headers, proxies, verify(SSL verification)]
 | 
					 | 
				
			||||||
        :param handlers: (optional) Array of message types for which
 | 
					 | 
				
			||||||
                         corresponding handlers will be called
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param chunk_size: (optional) Define the buffer size before data is
 | 
					 | 
				
			||||||
                           actually returned from the Streaming API. Default: 1
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.auth = OAuth1(app_key, app_secret,
 | 
					 | 
				
			||||||
                           oauth_token, oauth_token_secret)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client_args = client_args or {}
 | 
					 | 
				
			||||||
        default_headers = {'User-Agent': 'Twython Streaming v' + __version__}
 | 
					 | 
				
			||||||
        if 'headers' not in self.client_args:
 | 
					 | 
				
			||||||
            # If they didn't set any headers, set our defaults for them
 | 
					 | 
				
			||||||
            self.client_args['headers'] = default_headers
 | 
					 | 
				
			||||||
        elif 'User-Agent' not in self.client_args['headers']:
 | 
					 | 
				
			||||||
            # If they set headers, but didn't include User-Agent..
 | 
					 | 
				
			||||||
            # set it for them
 | 
					 | 
				
			||||||
            self.client_args['headers'].update(default_headers)
 | 
					 | 
				
			||||||
        self.client_args['timeout'] = timeout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.client = requests.Session()
 | 
					 | 
				
			||||||
        self.client.auth = self.auth
 | 
					 | 
				
			||||||
        self.client.stream = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Make a copy of the client args and iterate over them
 | 
					 | 
				
			||||||
        # Pop out all the acceptable args at this point because they will
 | 
					 | 
				
			||||||
        # Never be used again.
 | 
					 | 
				
			||||||
        client_args_copy = self.client_args.copy()
 | 
					 | 
				
			||||||
        for k, v in client_args_copy.items():
 | 
					 | 
				
			||||||
            if k in ('cert', 'headers', 'hooks', 'max_redirects', 'proxies'):
 | 
					 | 
				
			||||||
                setattr(self.client, k, v)
 | 
					 | 
				
			||||||
                self.client_args.pop(k)  # Pop, pop!
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.api_version = '1.1'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.retry_in = retry_in
 | 
					 | 
				
			||||||
        self.retry_count = retry_count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Set up type methods
 | 
					 | 
				
			||||||
        StreamTypes = TwythonStreamerTypes(self)
 | 
					 | 
				
			||||||
        self.statuses = StreamTypes.statuses
 | 
					 | 
				
			||||||
        self.user = StreamTypes.user
 | 
					 | 
				
			||||||
        self.site = StreamTypes.site
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.connected = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.handlers = handlers if handlers else \
 | 
					 | 
				
			||||||
            ['delete', 'limit', 'disconnect']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.chunk_size = chunk_size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _request(self, url, method='GET', params=None):
 | 
					 | 
				
			||||||
        """Internal stream request handling"""
 | 
					 | 
				
			||||||
        self.connected = True
 | 
					 | 
				
			||||||
        retry_counter = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        method = method.lower()
 | 
					 | 
				
			||||||
        func = getattr(self.client, method)
 | 
					 | 
				
			||||||
        params, _ = _transparent_params(params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def _send(retry_counter):
 | 
					 | 
				
			||||||
            requests_args = {}
 | 
					 | 
				
			||||||
            for k, v in self.client_args.items():
 | 
					 | 
				
			||||||
                # Maybe this should be set as a class
 | 
					 | 
				
			||||||
                # variable and only done once?
 | 
					 | 
				
			||||||
                if k in ('timeout', 'allow_redirects', 'verify'):
 | 
					 | 
				
			||||||
                    requests_args[k] = v
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            while self.connected:
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    if method == 'get':
 | 
					 | 
				
			||||||
                        requests_args['params'] = params
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        requests_args['data'] = params
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    response = func(url, **requests_args)
 | 
					 | 
				
			||||||
                except requests.exceptions.Timeout:
 | 
					 | 
				
			||||||
                    self.on_timeout()
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    if response.status_code != 200:
 | 
					 | 
				
			||||||
                        self.on_error(response.status_code, response.content)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if self.retry_count and \
 | 
					 | 
				
			||||||
                       (self.retry_count - retry_counter) > 0:
 | 
					 | 
				
			||||||
                        time.sleep(self.retry_in)
 | 
					 | 
				
			||||||
                        retry_counter += 1
 | 
					 | 
				
			||||||
                        _send(retry_counter)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return response
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while self.connected:
 | 
					 | 
				
			||||||
            response = _send(retry_counter)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for line in response.iter_lines(self.chunk_size):
 | 
					 | 
				
			||||||
                if not self.connected:
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
                if line:
 | 
					 | 
				
			||||||
                    try:
 | 
					 | 
				
			||||||
                        if is_py3:
 | 
					 | 
				
			||||||
                            line = line.decode('utf-8')
 | 
					 | 
				
			||||||
                        data = json.loads(line)
 | 
					 | 
				
			||||||
                    except ValueError:  # pragma: no cover
 | 
					 | 
				
			||||||
                        self.on_error(response.status_code,
 | 
					 | 
				
			||||||
                                      'Unable to decode response, \
 | 
					 | 
				
			||||||
                                      not valid JSON.')
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        if self.on_success(data):  # pragma: no cover
 | 
					 | 
				
			||||||
                            for message_type in self.handlers:
 | 
					 | 
				
			||||||
                                if message_type in data:
 | 
					 | 
				
			||||||
                                    handler = getattr(self,
 | 
					 | 
				
			||||||
                                                      'on_' + message_type,
 | 
					 | 
				
			||||||
                                                      None)
 | 
					 | 
				
			||||||
                                    if handler \
 | 
					 | 
				
			||||||
                                       and callable(handler) \
 | 
					 | 
				
			||||||
                                       and not handler(data.get(message_type)):
 | 
					 | 
				
			||||||
                                        break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        response.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def on_success(self, data):  # pragma: no cover
 | 
					 | 
				
			||||||
        """Called when data has been successfully received from the stream.
 | 
					 | 
				
			||||||
        Returns True if other handlers for this message should be invoked.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Feel free to override this to handle your streaming data how you
 | 
					 | 
				
			||||||
        want it handled. See
 | 
					 | 
				
			||||||
        https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/streaming-message-types
 | 
					 | 
				
			||||||
        for messages sent along in stream responses.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param data: data recieved from the stream
 | 
					 | 
				
			||||||
        :type data: dict
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def on_error(self, status_code, data):  # pragma: no cover
 | 
					 | 
				
			||||||
        """Called when stream returns non-200 status code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Feel free to override this to handle your streaming data how you
 | 
					 | 
				
			||||||
        want it handled.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param status_code: Non-200 status code sent from stream
 | 
					 | 
				
			||||||
        :type status_code: int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param data: Error message sent from stream
 | 
					 | 
				
			||||||
        :type data: dict
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def on_timeout(self):  # pragma: no cover
 | 
					 | 
				
			||||||
        """ Called when the request has timed out """
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def disconnect(self):
 | 
					 | 
				
			||||||
        """Used to disconnect the streaming client manually"""
 | 
					 | 
				
			||||||
        self.connected = False
 | 
					 | 
				
			||||||
@@ -1,108 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
twython.streaming.types
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This module contains classes and methods for :class:`TwythonStreamer` to use.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonStreamerTypes(object):
 | 
					 | 
				
			||||||
    """Class for different stream endpoints
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Not all streaming endpoints have nested endpoints.
 | 
					 | 
				
			||||||
    User Streams and Site Streams are single streams with no nested endpoints
 | 
					 | 
				
			||||||
    Status Streams include filter, sample and firehose endpoints
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self, streamer):
 | 
					 | 
				
			||||||
        self.streamer = streamer
 | 
					 | 
				
			||||||
        self.statuses = TwythonStreamerTypesStatuses(streamer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def user(self, **params):
 | 
					 | 
				
			||||||
        """Stream user
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://dev.twitter.com/docs/api/1.1/get/user
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        url = 'https://userstream.twitter.com/%s/user.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, params=params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def site(self, **params):
 | 
					 | 
				
			||||||
        """Stream site
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://dev.twitter.com/docs/api/1.1/get/site
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        url = 'https://sitestream.twitter.com/%s/site.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, params=params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwythonStreamerTypesStatuses(object):
 | 
					 | 
				
			||||||
    """Class for different statuses endpoints
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Available so :meth:`TwythonStreamer.statuses.filter()` is available.
 | 
					 | 
				
			||||||
    Just a bit cleaner than :meth:`TwythonStreamer.statuses_filter()`,
 | 
					 | 
				
			||||||
    :meth:`statuses_sample()`, etc. all being single methods in 
 | 
					 | 
				
			||||||
    :class:`TwythonStreamer`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self, streamer):
 | 
					 | 
				
			||||||
        self.streamer = streamer
 | 
					 | 
				
			||||||
        self.params = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def filter(self, **params):
 | 
					 | 
				
			||||||
        """Stream statuses/filter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param \*\*params: Parameters to send with your stream request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        url = 'https://stream.twitter.com/%s/statuses/filter.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, 'POST', params=params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def sample(self, **params):
 | 
					 | 
				
			||||||
        """Stream statuses/sample
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param \*\*params: Parameters to send with your stream request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://developer.twitter.com/en/docs/tweets/sample-realtime/api-reference/get-statuses-sample
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        url = 'https://stream.twitter.com/%s/statuses/sample.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, params=params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def firehose(self, **params):
 | 
					 | 
				
			||||||
        """Stream statuses/firehose
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param \*\*params: Parameters to send with your stream request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://dev.twitter.com/docs/api/1.1/get/statuses/firehose
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        url = 'https://stream.twitter.com/%s/statuses/firehose.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, params=params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_dynamic_filter(self, **params):
 | 
					 | 
				
			||||||
        """Set/update statuses/filter
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param \*\*params: Parameters to send with your stream request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Accepted params found at:
 | 
					 | 
				
			||||||
        https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.params = params
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dynamic_filter(self):
 | 
					 | 
				
			||||||
        """Stream statuses/filter with dynamic parameters"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        url = 'https://stream.twitter.com/%s/statuses/filter.json' \
 | 
					 | 
				
			||||||
              % self.streamer.api_version
 | 
					 | 
				
			||||||
        self.streamer._request(url, 'POST', params=self.params)
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user