]>
Commit | Line | Data |
---|---|---|
7364ea65 | 1 | |
2 | from base64 import b64encode | |
5251ea48 | 3 | from urllib import urlencode |
7364ea65 | 4 | |
5 | import httplib | |
7364ea65 | 6 | |
5251ea48 | 7 | from exceptions import Exception |
8 | ||
f1a8ed67 | 9 | def _py26OrGreater(): |
10 | import sys | |
11 | return sys.hexversion > 0x20600f0 | |
12 | ||
13 | if _py26OrGreater(): | |
14 | import json | |
15 | else: | |
16 | import simplejson as json | |
17 | ||
5251ea48 | 18 | class TwitterError(Exception): |
21e3bd23 | 19 | """ |
20 | Exception thrown by the Twitter object when there is an | |
21 | error interacting with twitter.com. | |
22 | """ | |
5251ea48 | 23 | pass |
24 | ||
7364ea65 | 25 | class TwitterCall(object): |
153dee29 | 26 | def __init__(self, username, password, format, domain, uri=""): |
7364ea65 | 27 | self.username = username |
28 | self.password = password | |
a55e6a11 | 29 | self.format = format |
7364ea65 | 30 | self.uri = uri |
153dee29 | 31 | self.domain = domain |
7364ea65 | 32 | def __getattr__(self, k): |
33 | try: | |
34 | return object.__getattr__(self, k) | |
35 | except AttributeError: | |
36 | return TwitterCall( | |
153dee29 | 37 | self.username, self.password, self.format, self.domain, |
a55e6a11 | 38 | self.uri + "/" + k) |
7364ea65 | 39 | def __call__(self, **kwargs): |
40 | method = "GET" | |
21e3bd23 | 41 | if (self.uri.endswith('new') |
42 | or self.uri.endswith('update') | |
25aecb69 | 43 | or self.uri.endswith('create') |
44 | or self.uri.endswith('destroy')): | |
7364ea65 | 45 | method = "POST" |
5b8b1ead | 46 | |
47 | encoded_kwargs = urlencode(kwargs.items()) | |
7364ea65 | 48 | argStr = "" |
5b8b1ead | 49 | if kwargs and (method == "GET"): |
50 | argStr = "?" + encoded_kwargs | |
51 | ||
52 | headers = {} | |
53 | if (self.username): | |
54 | headers["Authorization"] = "Basic " + b64encode("%s:%s" %( | |
55 | self.username, self.password)) | |
56 | if method == "POST": | |
57 | headers["Content-type"] = "application/x-www-form-urlencoded" | |
58 | headers["Content-length"] = len(encoded_kwargs) | |
59 | ||
153dee29 | 60 | c = httplib.HTTPConnection(self.domain) |
7364ea65 | 61 | try: |
5b8b1ead | 62 | c.putrequest(method, "%s.%s%s" %( |
a55e6a11 | 63 | self.uri, self.format, argStr)) |
5b8b1ead | 64 | for item in headers.iteritems(): |
65 | c.putheader(*item) | |
7364ea65 | 66 | c.endheaders() |
5b8b1ead | 67 | if method == "POST": |
68 | c.send(encoded_kwargs) | |
7364ea65 | 69 | r = c.getresponse() |
5b8b1ead | 70 | |
7364ea65 | 71 | if (r.status == 304): |
72 | return [] | |
73 | elif (r.status != 200): | |
5251ea48 | 74 | raise TwitterError("Twitter sent status %i: %s" %( |
7364ea65 | 75 | r.status, r.read())) |
5b8b1ead | 76 | if "json" == self.format: |
f1a8ed67 | 77 | return json.loads(r.read()) |
a55e6a11 | 78 | else: |
79 | return r.read() | |
7364ea65 | 80 | finally: |
81 | c.close() | |
82 | ||
83 | class Twitter(TwitterCall): | |
84 | """ | |
85 | The minimalist yet fully featured Twitter API class. | |
86 | ||
87 | Get RESTful data by accessing members of this class. The result | |
88 | is decoded python objects (lists and dicts). | |
89 | ||
90 | The Twitter API is documented here: | |
153dee29 HN |
91 | |
92 | http://apiwiki.twitter.com/ | |
93 | http://groups.google.com/group/twitter-development-talk/web/api-documentation | |
7364ea65 | 94 | |
95 | Examples:: | |
96 | ||
97 | twitter = Twitter("hello@foo.com", "password123") | |
98 | ||
99 | # Get the public timeline | |
100 | twitter.statuses.public_timeline() | |
101 | ||
102 | # Get a particular friend's timeline | |
103 | twitter.statuses.friends_timeline(id="billybob") | |
104 | ||
105 | # Also supported (but totally weird) | |
106 | twitter.statuses.friends_timeline.billybob() | |
107 | ||
108 | # Send a direct message | |
109 | twitter.direct_messages.new( | |
110 | user="billybob", | |
111 | text="I think yer swell!") | |
112 | ||
153dee29 HN |
113 | Searching Twitter:: |
114 | ||
115 | twitter_search = Twitter(domain="search.twitter.com") | |
116 | ||
117 | # Find the latest search trends | |
118 | twitter_search.trends() | |
119 | ||
120 | # Search for the latest News on #gaza | |
121 | twitter_search(q="#gaza") | |
122 | ||
7364ea65 | 123 | Using the data returned:: |
124 | ||
125 | Twitter API calls return decoded JSON. This is converted into | |
126 | a bunch of Python lists, dicts, ints, and strings. For example, | |
127 | ||
128 | x = twitter.statuses.public_timeline() | |
129 | ||
130 | # The first 'tweet' in the timeline | |
131 | x[0] | |
132 | ||
133 | # The screen name of the user who wrote the first 'tweet' | |
134 | x[0]['user']['screen_name'] | |
a55e6a11 | 135 | |
136 | Getting raw XML data:: | |
137 | ||
138 | If you prefer to get your Twitter data in XML format, pass | |
139 | format="xml" to the Twitter object when you instantiate it: | |
140 | ||
141 | twitter = Twitter(format="xml") | |
7364ea65 | 142 | |
a55e6a11 | 143 | The output will not be parsed in any way. It will be a raw string |
144 | of XML. | |
7364ea65 | 145 | """ |
153dee29 | 146 | def __init__(self, email=None, password=None, format="json", domain="twitter.com"): |
7364ea65 | 147 | """ |
148 | Create a new twitter API connector using the specified | |
a55e6a11 | 149 | credentials (email and password). Format specifies the output |
150 | format ("json" (default) or "xml"). | |
7364ea65 | 151 | """ |
a55e6a11 | 152 | if (format not in ("json", "xml")): |
153 | raise TwitterError("Unknown data format '%s'" %(format)) | |
153dee29 | 154 | TwitterCall.__init__(self, email, password, format, domain) |
7364ea65 | 155 | |
5b8b1ead | 156 | __all__ = ["Twitter", "TwitterError"] |