]>
Commit | Line | Data |
---|---|---|
a6a7f763 MV |
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:: | |
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 | |
9319fd3a | 36 | twitter.statuses.update(status='Hello, world!') |
a6a7f763 MV |
37 | |
38 | """ | |
39 | ||
40 | from __future__ import print_function | |
568331a9 MH |
41 | |
42 | from time import time | |
43 | from random import getrandbits | |
3930cc7b MV |
44 | |
45 | try: | |
46 | import urllib.parse as urllib_parse | |
aeabd5b8 | 47 | from urllib.parse import urlencode |
48 | PY3 = True | |
3930cc7b MV |
49 | except ImportError: |
50 | import urllib2 as urllib_parse | |
aeabd5b8 | 51 | from urllib import urlencode |
52 | PY3 = False | |
3930cc7b | 53 | |
568331a9 MH |
54 | import hashlib |
55 | import hmac | |
a2855195 | 56 | import base64 |
568331a9 | 57 | |
a6a7f763 | 58 | from .auth import Auth |
3930cc7b MV |
59 | |
60 | ||
1b31d642 | 61 | def 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 | ||
70 | def 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 | 78 | class 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. | |
13f259cf | 124 | # In Python2, since there is no safe option for urlencode, we force it by hand |
568331a9 | 125 | def urlencode_noplus(query): |
aeabd5b8 | 126 | if not PY3: |
127 | new_query = [] | |
13f259cf | 128 | TILDE = '____TILDE-PYTHON-TWITTER____' |
aeabd5b8 | 129 | for k,v in query: |
130 | if type(k) is unicode: k = k.encode('utf-8') | |
d290f579 | 131 | k = str(k).replace("~", TILDE) |
aeabd5b8 | 132 | if type(v) is unicode: v = v.encode('utf-8') |
d290f579 | 133 | v = str(v).replace("~", TILDE) |
aeabd5b8 | 134 | new_query.append((k, v)) |
135 | query = new_query | |
13f259cf | 136 | return urlencode(query).replace(TILDE, "~").replace("+", "%20") |
be5f32da | 137 | |
13f259cf | 138 | return urlencode(query, safe='~').replace("+", "%20") |