]> jfr.im git - z_archive/twitter.git/blob - twitter/oauth.py
Use Six's method to determine when running under Python 3.
[z_archive/twitter.git] / twitter / oauth.py
1 """
2 Visit the Twitter developer page and create a new application:
3
4 https://dev.twitter.com/apps/new
5
6 This will get you a CONSUMER_KEY and CONSUMER_SECRET.
7
8 When users run your application they have to authenticate your app
9 with their Twitter account. A few HTTP calls to twitter are required
10 to do this. Please see the twitter.oauth_dance module to see how this
11 is done. If you are making a command-line app, you can use the
12 oauth_dance() function directly.
13
14 Performing the "oauth dance" gets you an ouath token and oauth secret
15 that authenticate the user with Twitter. You should save these for
16 later so that the user doesn't have to do the oauth dance again.
17
18 read_token_file and write_token_file are utility methods to read and
19 write OAuth token and secret key values. The values are stored as
20 strings in the file. Not terribly exciting.
21
22 Finally, you can use the OAuth authenticator to connect to Twitter. In
23 code it all goes like this::
24
25 from twitter import *
26
27 MY_TWITTER_CREDS = os.path.expanduser('~/.my_app_credentials')
28 if not os.path.exists(MY_TWITTER_CREDS):
29 oauth_dance("My App Name", CONSUMER_KEY, CONSUMER_SECRET,
30 MY_TWITTER_CREDS)
31
32 oauth_token, oauth_secret = read_token_file(MY_TWITTER_CREDS)
33
34 twitter = Twitter(auth=OAuth(
35 oauth_token, oauth_token_secret, CONSUMER_KEY, CONSUMER_SECRET))
36
37 # Now work with Twitter
38 twitter.statuses.update(status='Hello, world!')
39
40 """
41
42 from __future__ import print_function
43
44 from random import getrandbits
45 import sys
46 from time import time
47
48 PY3 = sys.version_info[0] == 3
49
50 try:
51 import urllib.parse as urllib_parse
52 from urllib.parse import urlencode
53 except ImportError:
54 import urllib2 as urllib_parse
55 from urllib import urlencode
56
57 import hashlib
58 import hmac
59 import base64
60
61 from .auth import Auth
62
63
64 def write_token_file(filename, oauth_token, oauth_token_secret):
65 """
66 Write a token file to hold the oauth token and oauth token secret.
67 """
68 oauth_file = open(filename, 'w')
69 print(oauth_token, file=oauth_file)
70 print(oauth_token_secret, file=oauth_file)
71 oauth_file.close()
72
73 def read_token_file(filename):
74 """
75 Read a token file and return the oauth token and oauth token secret.
76 """
77 f = open(filename)
78 return f.readline().strip(), f.readline().strip()
79
80
81 class OAuth(Auth):
82 """
83 An OAuth authenticator.
84 """
85 def __init__(self, token, token_secret, consumer_key, consumer_secret):
86 """
87 Create the authenticator. If you are in the initial stages of
88 the OAuth dance and don't yet have a token or token_secret,
89 pass empty strings for these params.
90 """
91 self.token = token
92 self.token_secret = token_secret
93 self.consumer_key = consumer_key
94 self.consumer_secret = consumer_secret
95
96 def encode_params(self, base_url, method, params):
97 params = params.copy()
98
99 if self.token:
100 params['oauth_token'] = self.token
101
102 params['oauth_consumer_key'] = self.consumer_key
103 params['oauth_signature_method'] = 'HMAC-SHA1'
104 params['oauth_version'] = '1.0'
105 params['oauth_timestamp'] = str(int(time()))
106 params['oauth_nonce'] = str(getrandbits(64))
107
108 enc_params = urlencode_noplus(sorted(params.items()))
109
110 key = self.consumer_secret + "&" + urllib_parse.quote(self.token_secret, safe='~')
111
112 message = '&'.join(
113 urllib_parse.quote(i, safe='~') for i in [method.upper(), base_url, enc_params])
114
115 signature = (base64.b64encode(hmac.new(
116 key.encode('ascii'), message.encode('ascii'), hashlib.sha1)
117 .digest()))
118 return enc_params + "&" + "oauth_signature=" + urllib_parse.quote(signature, safe='~')
119
120 def generate_headers(self):
121 return {}
122
123 # apparently contrary to the HTTP RFCs, spaces in arguments must be encoded as
124 # %20 rather than '+' when constructing an OAuth signature (and therefore
125 # also in the request itself.)
126 # So here is a specialized version which does exactly that.
127 # In Python2, since there is no safe option for urlencode, we force it by hand
128 def urlencode_noplus(query):
129 if not PY3:
130 new_query = []
131 TILDE = '____TILDE-PYTHON-TWITTER____'
132 for k,v in query:
133 if type(k) is unicode: k = k.encode('utf-8')
134 k = str(k).replace("~", TILDE)
135 if type(v) is unicode: v = v.encode('utf-8')
136 v = str(v).replace("~", TILDE)
137 new_query.append((k, v))
138 query = new_query
139 return urlencode(query).replace(TILDE, "~").replace("+", "%20")
140
141 return urlencode(query, safe='~').replace("+", "%20")