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