]>
Commit | Line | Data |
---|---|---|
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 | 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(status='Hello, world!') | |
37 | ||
38 | """ | |
39 | ||
40 | from __future__ import print_function | |
41 | ||
42 | from time import time | |
43 | from random import getrandbits | |
44 | ||
45 | try: | |
46 | import urllib.parse as urllib_parse | |
47 | from urllib.parse import urlencode | |
48 | PY3 = True | |
49 | except ImportError: | |
50 | import urllib2 as urllib_parse | |
51 | from urllib import urlencode | |
52 | PY3 = False | |
53 | ||
54 | import hashlib | |
55 | import hmac | |
56 | import base64 | |
57 | ||
58 | from .auth import Auth | |
59 | ||
60 | ||
61 | def write_token_file(filename, oauth_token, oauth_token_secret): | |
62 | """ | |
63 | Write a token file to hold the oauth token and oauth token secret. | |
64 | """ | |
65 | oauth_file = open(filename, 'w') | |
66 | print(oauth_token, file=oauth_file) | |
67 | print(oauth_token_secret, file=oauth_file) | |
68 | oauth_file.close() | |
69 | ||
70 | def read_token_file(filename): | |
71 | """ | |
72 | Read a token file and return the oauth token and oauth token secret. | |
73 | """ | |
74 | f = open(filename) | |
75 | return f.readline().strip(), f.readline().strip() | |
76 | ||
77 | ||
78 | class OAuth(Auth): | |
79 | """ | |
80 | An OAuth authenticator. | |
81 | """ | |
82 | def __init__(self, token, token_secret, consumer_key, consumer_secret): | |
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 | """ | |
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 | ||
96 | if self.token: | |
97 | params['oauth_token'] = self.token | |
98 | ||
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 | ||
105 | enc_params = urlencode_noplus(sorted(params.items())) | |
106 | ||
107 | key = self.consumer_secret + "&" + urllib_parse.quote(self.token_secret, safe='~') | |
108 | ||
109 | message = '&'.join( | |
110 | urllib_parse.quote(i, safe='~') for i in [method.upper(), base_url, enc_params]) | |
111 | ||
112 | signature = (base64.b64encode(hmac.new( | |
113 | key.encode('ascii'), message.encode('ascii'), hashlib.sha1) | |
114 | .digest())) | |
115 | return enc_params + "&" + "oauth_signature=" + urllib_parse.quote(signature, safe='~') | |
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. | |
124 | # In Python2, since there is no safe option for urlencode, we force it by hand | |
125 | def urlencode_noplus(query): | |
126 | if not PY3: | |
127 | new_query = [] | |
128 | TILDE = '____TILDE-PYTHON-TWITTER____' | |
129 | for k,v in query: | |
130 | if type(k) is unicode: k = k.encode('utf-8') | |
131 | k = str(k).replace("~", TILDE) | |
132 | if type(v) is unicode: v = v.encode('utf-8') | |
133 | v = str(v).replace("~", TILDE) | |
134 | new_query.append((k, v)) | |
135 | query = new_query | |
136 | return urlencode(query).replace(TILDE, "~").replace("+", "%20") | |
137 | ||
138 | return urlencode(query, safe='~').replace("+", "%20") |