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