10
10
"""
11
11
12
12
import requests
13
- from requests_oauthlib import OAuth1
13
+ from requests .auth import HTTPBasicAuth
14
+ from requests_oauthlib import OAuth1 , OAuth2
14
15
15
16
from . import __version__
16
17
from .compat import json , urlencode , parse_qsl , quote_plus , str , is_py2
21
22
22
23
class Twython (EndpointsMixin , object ):
23
24
def __init__ (self , app_key = None , app_secret = None , oauth_token = None ,
24
- oauth_token_secret = None , headers = None , proxies = None ,
25
- api_version = '1.1' , ssl_verify = True ):
25
+ oauth_token_secret = None , access_token = None , token_type = 'bearer' ,
26
+ oauth_version = 1 , headers = None , proxies = None , api_version = '1.1' ,
27
+ ssl_verify = True ):
26
28
"""Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below).
27
29
28
30
:param app_key: (optional) Your applications key
29
31
:param app_secret: (optional) Your applications secret key
30
- :param oauth_token: (optional) Used with oauth_token_secret to make authenticated calls
31
- :param oauth_token_secret: (optional) Used with oauth_token to make authenticated calls
32
+ :param oauth_token: (optional) When using **OAuth 1**, combined with oauth_token_secret to make authenticated calls
33
+ :param oauth_token_secret: (optional) When using **OAuth 1** combined with oauth_token to make authenticated calls
34
+ :param access_token: (optional) When using **OAuth 2**, provide a valid access token if you have one
35
+ :param token_type: (optional) When using **OAuth 2**, provide your token type. Default: bearer
36
+ :param oauth_version: (optional) Choose which OAuth version to use. Default: 1
37
+
32
38
:param headers: (optional) Custom headers to send along with the request
33
39
:param proxies: (optional) A dictionary of proxies, for example {"http":"proxy.example.org:8080", "https":"proxy.example.org:8081"}.
34
40
:param ssl_verify: (optional) Turns off ssl verification when False. Useful if you have development server issues.
@@ -38,14 +44,24 @@ def __init__(self, app_key=None, app_secret=None, oauth_token=None,
38
44
# API urls, OAuth urls and API version; needed for hitting that there API.
39
45
self .api_version = api_version
40
46
self .api_url = 'https://api.twitter.com/%s'
41
- self .request_token_url = self .api_url % 'oauth/request_token'
42
- self .access_token_url = self .api_url % 'oauth/access_token'
43
- self .authenticate_url = self .api_url % 'oauth/authenticate'
44
47
45
48
self .app_key = app_key
46
49
self .app_secret = app_secret
47
50
self .oauth_token = oauth_token
48
51
self .oauth_token_secret = oauth_token_secret
52
+ self .access_token = access_token
53
+
54
+ # OAuth 1
55
+ self .request_token_url = self .api_url % 'oauth/request_token'
56
+ self .access_token_url = self .api_url % 'oauth/access_token'
57
+ self .authenticate_url = self .api_url % 'oauth/authenticate'
58
+
59
+ if self .access_token : # If they pass an access token, force OAuth 2
60
+ oauth_version = 2
61
+
62
+ # OAuth 2
63
+ if oauth_version == 2 :
64
+ self .request_token_url = self .api_url % 'oauth2/token'
49
65
50
66
req_headers = {'User-Agent' : 'Twython v' + __version__ }
51
67
if headers :
@@ -55,14 +71,20 @@ def __init__(self, app_key=None, app_secret=None, oauth_token=None,
55
71
# If no keys/tokens are passed to __init__, auth=None allows for
56
72
# unauthenticated requests, although I think all v1.1 requests need auth
57
73
auth = None
58
- if self .app_key is not None and self .app_secret is not None and \
59
- self .oauth_token is None and self .oauth_token_secret is None :
60
- auth = OAuth1 (self .app_key , self .app_secret )
61
-
62
- if self .app_key is not None and self .app_secret is not None and \
63
- self .oauth_token is not None and self .oauth_token_secret is not None :
64
- auth = OAuth1 (self .app_key , self .app_secret ,
65
- self .oauth_token , self .oauth_token_secret )
74
+ if oauth_version == 1 :
75
+ # User Authentication is through OAuth 1
76
+ if self .app_key is not None and self .app_secret is not None and \
77
+ self .oauth_token is None and self .oauth_token_secret is None :
78
+ auth = OAuth1 (self .app_key , self .app_secret )
79
+
80
+ if self .app_key is not None and self .app_secret is not None and \
81
+ self .oauth_token is not None and self .oauth_token_secret is not None :
82
+ auth = OAuth1 (self .app_key , self .app_secret ,
83
+ self .oauth_token , self .oauth_token_secret )
84
+ elif oauth_version == 2 and self .access_token :
85
+ # Application Authentication is through OAuth 2
86
+ token = {'token_type' : token_type , 'access_token' : self .access_token }
87
+ auth = OAuth2 (self .app_key , token = token )
66
88
67
89
self .client = requests .Session ()
68
90
self .client .headers = req_headers
@@ -205,6 +227,9 @@ def get_authentication_tokens(self, callback_url=None, force_login=False, screen
205
227
:param app_secret: (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
206
228
:rtype: dict
207
229
"""
230
+ if self .oauth_version != 1 :
231
+ raise TwythonError ('This method can only be called when your OAuth version is 1.0.' )
232
+
208
233
callback_url = callback_url or self .callback_url
209
234
request_args = {}
210
235
if callback_url :
@@ -247,18 +272,39 @@ def get_authorized_tokens(self, oauth_verifier):
247
272
:rtype: dict
248
273
249
274
"""
275
+ if self .oauth_version != 1 :
276
+ raise TwythonError ('This method can only be called when your OAuth version is 1.0.' )
277
+
250
278
response = self .client .get (self .access_token_url , params = {'oauth_verifier' : oauth_verifier })
251
279
authorized_tokens = dict (parse_qsl (response .content .decode ('utf-8' )))
252
280
if not authorized_tokens :
253
281
raise TwythonError ('Unable to decode authorized tokens.' )
254
282
255
283
return authorized_tokens
256
284
257
- # ------------------------------------------------------------------------------------------------------------------------
258
- # The following methods are all different in some manner or require special attention with regards to the Twitter API.
259
- # Because of this, we keep them separate from all the other endpoint definitions - ideally this should be change-able,
260
- # but it's not high on the priority list at the moment.
261
- # ------------------------------------------------------------------------------------------------------------------------
285
+ def obtain_access_token (self ):
286
+ """Returns an OAuth 2 access token to make OAuth 2 authenticated read-only calls.
287
+
288
+ :rtype: string
289
+ """
290
+ if self .oauth_version != 2 :
291
+ raise TwythonError ('This method can only be called when your OAuth version is 2.0.' )
292
+
293
+ data = {'grant_type' : 'client_credentials' }
294
+ basic_auth = HTTPBasicAuth (self .app_key , self .app_secret )
295
+ try :
296
+ response = self .client .post (self .request_token_url ,
297
+ data = data , auth = basic_auth )
298
+ content = response .content .decode ('utf-8' )
299
+ try :
300
+ content = content .json ()
301
+ except AttributeError :
302
+ content = json .loads (content )
303
+ access_token = content ['access_token' ]
304
+ except (ValueError , requests .exceptions .RequestException ):
305
+ raise TwythonAuthError ('Unable to obtain OAuth 2 access token.' )
306
+ else :
307
+ return access_token
262
308
263
309
@staticmethod
264
310
def construct_api_url (api_url , ** params ):
0 commit comments