Re-implement the GetFriends() of python-twitter with paging logic so that all friends...
[hermes] / package / src / org / maemo / hermes / engine / twitter / api.py
1 from org.maemo.hermes.engine.twitter.user import User
2 import urllib, urllib2
3 import base64
4 import simplejson
5 import urlparse
6
7 class TwitterApi():
8     """Twitter backend for Hermes. Inspired by
9           http://code.google.com/p/python-twitter/source/browse/twitter.py
10        
11        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
12        Released under the Artistic Licence."""
13        
14        
15     # -----------------------------------------------------------------------
16     def __init__(self, username, password):
17         self._username = username
18         self._password = password
19
20
21     # -----------------------------------------------------------------------
22     def get_friends(self):
23         '''Return the full list of people being followed by 'username'.'''
24
25         url = 'https://twitter.com/statuses/friends.json'
26         cursor = -1
27         users = []
28         while True:
29             json = self._FetchUrl(url, parameters = { 'cursor': cursor})
30             data = simplejson.loads(json)
31             if 'error' in data:
32                 raise Exception(data['error'])
33
34             for x in data['users']:
35                 users.append(User.NewFromJsonDict(x))
36
37             cursor = data['next_cursor']
38             if cursor <= data['previous_cursor']:
39                 break
40
41         return users
42
43
44     # -----------------------------------------------------------------------
45     def _FetchUrl(self,
46                   url,
47                   parameters=None):
48         '''Fetch a URL, optionally caching for a specified time.
49
50         Args:
51           url:
52             The URL to retrieve
53           parameters:
54             A dict whose key/value pairs should encoded and added
55             to the query string. [optional]
56
57         Returns:
58           A string containing the body of the response.
59         '''
60
61         # Build the extra parameters dict
62         extra_params = {}
63         if parameters:
64             extra_params.update(parameters)
65
66         # Add key/value parameters to the query string of the url
67         url = self._BuildUrl(url, extra_params=extra_params)
68
69         # Get a url opener that can handle basic auth
70         basic_auth = base64.encodestring('%s:%s' % (self._username, self._password))[:-1]
71
72         handler = urllib2.HTTPBasicAuthHandler()
73         (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
74         handler.add_password('Twitter API', netloc, self._username, self._password)
75         opener = urllib2.build_opener(handler)
76         opener.addheaders = {'Authorization': 'Basic %s' % basic_auth}.items()
77     
78         url_data = opener.open(url, None).read()
79         opener.close()
80         return url_data
81
82
83     # -----------------------------------------------------------------------
84     def _BuildUrl(self, url, path_elements=None, extra_params=None):
85         # Break url into consituent parts
86         (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
87
88         # Add any additional path elements to the path
89         if path_elements:
90             # Filter out the path elements that have a value of None
91             p = [i for i in path_elements if i]
92             if not path.endswith('/'):
93                 path += '/'
94             path += '/'.join(p)
95
96         # Add any additional query parameters to the query string
97         if extra_params and len(extra_params) > 0:
98             extra_query = self._EncodeParameters(extra_params)
99             # Add it to the existing query
100             if query:
101                 query += '&' + extra_query
102             else:
103                 query = extra_query
104
105         # Return the rebuilt URL
106         return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
107
108
109
110     # -----------------------------------------------------------------------
111     def _EncodeParameters(self, parameters):
112         '''Return a string in key=value&key=value form
113
114         Values of None are not included in the output string.
115
116         Args:
117           parameters:
118             A dict of (key, value) tuples, where value is encoded as
119             specified by self._encoding
120         Returns:
121           A URL-encoded string in "key=value&key=value" form
122         '''
123         if parameters is None:
124             return None
125         else:
126             return urllib.urlencode(dict([(k, unicode(v).encode('utf-8')) for k, v in parameters.items() if v is not None]))
127