]> jfr.im git - z_archive/twitter.git/blob - twitter/oauth.py
Version 1.17.0
[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 from time import time
46
47 from .util import PY_3_OR_HIGHER
48
49 try:
50 import urllib.parse as urllib_parse
51 from urllib.parse import urlencode
52 except ImportError:
53 import urllib2 as urllib_parse
54 from urllib import urlencode
55
56 import hashlib
57 import hmac
58 import base64
59
60 from .auth import Auth, MissingCredentialsError
61
62
63 def write_token_file(filename, oauth_token, oauth_token_secret):
64 """
65 Write a token file to hold the oauth token and oauth token secret.
66 """
67 oauth_file = open(filename, 'w')
68 print(oauth_token, file=oauth_file)
69 print(oauth_token_secret, file=oauth_file)
70 oauth_file.close()
71
72 def read_token_file(filename):
73 """
74 Read a token file and return the oauth token and oauth token secret.
75 """
76 f = open(filename)
77 return f.readline().strip(), f.readline().strip()
78
79
80 class OAuth(Auth):
81 """
82 An OAuth authenticator.
83 """
84 def __init__(self, token, token_secret, consumer_key, consumer_secret):
85 """
86 Create the authenticator. If you are in the initial stages of
87 the OAuth dance and don't yet have a token or token_secret,
88 pass empty strings for these params.
89 """
90 self.token = token
91 self.token_secret = token_secret
92 self.consumer_key = consumer_key
93 self.consumer_secret = consumer_secret
94
95 if token_secret is None or consumer_secret is None:
96 raise MissingCredentialsError(
97 'You must supply strings for token_secret and consumer_secret, not None.')
98
99 def encode_params(self, base_url, method, params):
100 params = params.copy()
101
102 if self.token:
103 params['oauth_token'] = self.token
104
105 params['oauth_consumer_key'] = self.consumer_key
106 params['oauth_signature_method'] = 'HMAC-SHA1'
107 params['oauth_version'] = '1.0'
108 params['oauth_timestamp'] = str(int(time()))
109 params['oauth_nonce'] = str(getrandbits(64))
110
111 enc_params = urlencode_noplus(sorted(params.items()))
112
113 key = self.consumer_secret + "&" + urllib_parse.quote(self.token_secret, safe='~')
114
115 message = '&'.join(
116 urllib_parse.quote(i, safe='~') for i in [method.upper(), base_url, enc_params])
117
118 signature = (base64.b64encode(hmac.new(
119 key.encode('ascii'), message.encode('ascii'), hashlib.sha1)
120 .digest()))
121 return enc_params + "&" + "oauth_signature=" + urllib_parse.quote(signature, safe='~')
122
123 def generate_headers(self):
124 return {}
125
126 # apparently contrary to the HTTP RFCs, spaces in arguments must be encoded as
127 # %20 rather than '+' when constructing an OAuth signature (and therefore
128 # also in the request itself.)
129 # So here is a specialized version which does exactly that.
130 # In Python2, since there is no safe option for urlencode, we force it by hand
131 def urlencode_noplus(query):
132 if not PY_3_OR_HIGHER:
133 new_query = []
134 TILDE = '____TILDE-PYTHON-TWITTER____'
135 for k,v in query:
136 if type(k) is unicode: k = k.encode('utf-8')
137 k = str(k).replace("~", TILDE)
138 if type(v) is unicode: v = v.encode('utf-8')
139 v = str(v).replace("~", TILDE)
140 new_query.append((k, v))
141 query = new_query
142 return urlencode(query).replace(TILDE, "~").replace("+", "%20")
143
144 return urlencode(query, safe='~').replace("+", "%20")