From adab94b240bf10d8aa7e89fcdf6fae41e21281a9 Mon Sep 17 00:00:00 2001 From: Ryan McGrath Date: Thu, 6 Aug 2009 02:43:40 -0400 Subject: [PATCH] There, *now* this build should be fixed. --- __init__.py | 1 + twython.py => build/lib/twython2k.py | 2 +- dist/twython-0.5-py2.5.egg | Bin 29112 -> 42140 bytes setup.py | 2 +- twython.egg-info/SOURCES.txt | 2 +- twython.egg-info/top_level.txt | 2 +- twython2k.py | 654 +++++++++++++++++++++++++++ 7 files changed, 659 insertions(+), 4 deletions(-) create mode 100644 __init__.py rename twython.py => build/lib/twython2k.py (99%) create mode 100644 twython2k.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..8829c09 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +import twython2k as twython diff --git a/twython.py b/build/lib/twython2k.py similarity index 99% rename from twython.py rename to build/lib/twython2k.py index ea0ec31..051884c 100644 --- a/twython.py +++ b/build/lib/twython2k.py @@ -60,7 +60,6 @@ class setup: self.access_token_url = 'https://twitter.com/oauth/access_token' self.authorization_url = 'http://twitter.com/oauth/authorize' self.signin_url = 'http://twitter.com/oauth/authenticate' - self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.request_token = None @@ -81,6 +80,7 @@ class setup: except HTTPError, e: raise TangoError("Authentication failed with your provided credentials. Try again? (%s failure)" % `e.code`, e.code) else: + self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1() # Awesome OAuth authentication ritual if consumer_secret is not None and consumer_key is not None: #req = oauth.OAuthRequest.from_consumer_and_token diff --git a/dist/twython-0.5-py2.5.egg b/dist/twython-0.5-py2.5.egg index c81e15bb706bc0594009b0ea771a40a94d5fca7c..9911338a46920b14c5f533d0001589fcb3c05669 100644 GIT binary patch delta 13116 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 KNNmA>1pY7LE5}p- delta 838 zcmbPpl4-|d#tmA+OiOq->j?8JGA-tvT$N!1q&Dx!$YKR?Rq{M|m+-P#=c+a>Ol4qT zI6k>D&xG|=GS|zclQ-oB@-7C-rFmFA;sVNXOcuyj;eX7;5a7+sA_5X%Si(EmHQ%5f zWP+Or(+X3dpg9l=193@tWl2VUo_>6MW?p7Ve7s&kWq1GiGrA}AwL1H+2WXw}>(}?v zJ*Thh<)<5V>58|f?>SE$uio?CXMA<_Jbe7FcCc@L{MeKc*)lfkNgpOH+6pxN5fFo% z0Jfzb)tY42USF<51_G^U$@7}JXfT?K=IM7R%w2LTPZ6JF5~poFdoA93VN8d9{1&l&Uis$lVST~U^-(9YR@P{g zs`l&`{T#Vd>q^q*v 140: + raise TangoError("This status message is over 140 characters. Trim it down!") + try: + return simplejson.load(self.opener.open("http://twitter.com/statuses/update.json?", urllib.urlencode({"status": status, "in_reply_to_status_id": in_reply_to_status_id}))) + except HTTPError, e: + raise TangoError("updateStatus() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("updateStatus() requires you to be authenticated.") + + def destroyStatus(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/status/destroy/%s.json", "POST" % id)) + except HTTPError, e: + raise TangoError("destroyStatus() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("destroyStatus() requires you to be authenticated.") + + def endSession(self): + if self.authenticated is True: + try: + self.opener.open("http://twitter.com/account/end_session.json", "") + self.authenticated = False + except HTTPError, e: + raise TangoError("endSession failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("You can't end a session when you're not authenticated to begin with.") + + def getDirectMessages(self, since_id = None, max_id = None, count = None, page = "1"): + if self.authenticated is True: + apiURL = "http://twitter.com/direct_messages.json?page=" + page + if since_id is not None: + apiURL += "&since_id=" + since_id + if max_id is not None: + apiURL += "&max_id=" + max_id + if count is not None: + apiURL += "&count=" + count + + try: + return simplejson.load(self.opener.open(apiURL)) + except HTTPError, e: + raise TangoError("getDirectMessages() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getDirectMessages() requires you to be authenticated.") + + def getSentMessages(self, since_id = None, max_id = None, count = None, page = "1"): + if self.authenticated is True: + apiURL = "http://twitter.com/direct_messages/sent.json?page=" + page + if since_id is not None: + apiURL += "&since_id=" + since_id + if max_id is not None: + apiURL += "&max_id=" + max_id + if count is not None: + apiURL += "&count=" + count + + try: + return simplejson.load(self.opener.open(apiURL)) + except HTTPError, e: + raise TangoError("getSentMessages() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getSentMessages() requires you to be authenticated.") + + def sendDirectMessage(self, user, text): + if self.authenticated is True: + if len(list(text)) < 140: + try: + return self.opener.open("http://twitter.com/direct_messages/new.json", urllib.urlencode({"user": user, "text": text})) + except HTTPError, e: + raise TangoError("sendDirectMessage() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("Your message must not be longer than 140 characters") + else: + raise TangoError("You must be authenticated to send a new direct message.") + + def destroyDirectMessage(self, id): + if self.authenticated is True: + try: + return self.opener.open("http://twitter.com/direct_messages/destroy/%s.json" % id, "") + except HTTPError, e: + raise TangoError("destroyDirectMessage() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("You must be authenticated to destroy a direct message.") + + def createFriendship(self, id = None, user_id = None, screen_name = None, follow = "false"): + if self.authenticated is True: + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/friendships/create/" + id + ".json" + "?follow=" + follow + if user_id is not None: + apiURL = "http://twitter.com/friendships/create.json?user_id=" + user_id + "&follow=" + follow + if screen_name is not None: + apiURL = "http://twitter.com/friendships/create.json?screen_name=" + screen_name + "&follow=" + follow + try: + return simplejson.load(self.opener.open(apiURL)) + except HTTPError, e: + # Rate limiting is done differently here for API reasons... + if e.code == 403: + raise TangoError("You've hit the update limit for this method. Try again in 24 hours.") + raise TangoError("createFriendship() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("createFriendship() requires you to be authenticated.") + + def destroyFriendship(self, id = None, user_id = None, screen_name = None): + if self.authenticated is True: + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/friendships/destroy/" + id + ".json" + if user_id is not None: + apiURL = "http://twitter.com/friendships/destroy.json?user_id=" + user_id + if screen_name is not None: + apiURL = "http://twitter.com/friendships/destroy.json?screen_name=" + screen_name + try: + return simplejson.load(self.opener.open(apiURL)) + except HTTPError, e: + raise TangoError("destroyFriendship() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("destroyFriendship() requires you to be authenticated.") + + def checkIfFriendshipExists(self, user_a, user_b): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/friendships/exists.json", urllib.urlencode({"user_a": user_a, "user_b": user_b}))) + except HTTPError, e: + raise TangoError("checkIfFriendshipExists() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("checkIfFriendshipExists(), oddly, requires that you be authenticated.") + + def updateDeliveryDevice(self, device_name = "none"): + if self.authenticated is True: + try: + return self.opener.open("http://twitter.com/account/update_delivery_device.json?", urllib.urlencode({"device": device_name})) + except HTTPError, e: + raise TangoError("updateDeliveryDevice() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("updateDeliveryDevice() requires you to be authenticated.") + + def updateProfileColors(self, **kwargs): + if self.authenticated is True: + try: + return self.opener.open(self.constructApiURL("http://twitter.com/account/update_profile_colors.json?", kwargs)) + except HTTPError, e: + raise TangoError("updateProfileColors() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("updateProfileColors() requires you to be authenticated.") + + def updateProfile(self, name = None, email = None, url = None, location = None, description = None): + if self.authenticated is True: + useAmpersands = False + updateProfileQueryString = "" + if name is not None: + if len(list(name)) < 20: + updateProfileQueryString += "name=" + name + useAmpersands = True + else: + raise TangoError("Twitter has a character limit of 20 for all usernames. Try again.") + if email is not None and "@" in email: + if len(list(email)) < 40: + if useAmpersands is True: + updateProfileQueryString += "&email=" + email + else: + updateProfileQueryString += "email=" + email + useAmpersands = True + else: + raise TangoError("Twitter has a character limit of 40 for all email addresses, and the email address must be valid. Try again.") + if url is not None: + if len(list(url)) < 100: + if useAmpersands is True: + updateProfileQueryString += "&" + urllib.urlencode({"url": url}) + else: + updateProfileQueryString += urllib.urlencode({"url": url}) + useAmpersands = True + else: + raise TangoError("Twitter has a character limit of 100 for all urls. Try again.") + if location is not None: + if len(list(location)) < 30: + if useAmpersands is True: + updateProfileQueryString += "&" + urllib.urlencode({"location": location}) + else: + updateProfileQueryString += urllib.urlencode({"location": location}) + useAmpersands = True + else: + raise TangoError("Twitter has a character limit of 30 for all locations. Try again.") + if description is not None: + if len(list(description)) < 160: + if useAmpersands is True: + updateProfileQueryString += "&" + urllib.urlencode({"description": description}) + else: + updateProfileQueryString += urllib.urlencode({"description": description}) + else: + raise TangoError("Twitter has a character limit of 160 for all descriptions. Try again.") + + if updateProfileQueryString != "": + try: + return self.opener.open("http://twitter.com/account/update_profile.json?", updateProfileQueryString) + except HTTPError, e: + raise TangoError("updateProfile() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("updateProfile() requires you to be authenticated.") + + def getFavorites(self, page = "1"): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/favorites.json?page=" + page)) + except HTTPError, e: + raise TangoError("getFavorites() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getFavorites() requires you to be authenticated.") + + def createFavorite(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/favorites/create/" + id + ".json", "")) + except HTTPError, e: + raise TangoError("createFavorite() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("createFavorite() requires you to be authenticated.") + + def destroyFavorite(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/favorites/destroy/" + id + ".json", "")) + except HTTPError, e: + raise TangoError("destroyFavorite() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("destroyFavorite() requires you to be authenticated.") + + def notificationFollow(self, id = None, user_id = None, screen_name = None): + if self.authenticated is True: + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/notifications/follow/" + id + ".json" + if user_id is not None: + apiURL = "http://twitter.com/notifications/follow/follow.json?user_id=" + user_id + if screen_name is not None: + apiURL = "http://twitter.com/notifications/follow/follow.json?screen_name=" + screen_name + try: + return simplejson.load(self.opener.open(apiURL, "")) + except HTTPError, e: + raise TangoError("notificationFollow() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("notificationFollow() requires you to be authenticated.") + + def notificationLeave(self, id = None, user_id = None, screen_name = None): + if self.authenticated is True: + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/notifications/leave/" + id + ".json" + if user_id is not None: + apiURL = "http://twitter.com/notifications/leave/leave.json?user_id=" + user_id + if screen_name is not None: + apiURL = "http://twitter.com/notifications/leave/leave.json?screen_name=" + screen_name + try: + return simplejson.load(self.opener.open(apiURL, "")) + except HTTPError, e: + raise TangoError("notificationLeave() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("notificationLeave() requires you to be authenticated.") + + def getFriendsIDs(self, id = None, user_id = None, screen_name = None, page = "1"): + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/friends/ids/" + id + ".json" + "?page=" + page + if user_id is not None: + apiURL = "http://twitter.com/friends/ids.json?user_id=" + user_id + "&page=" + page + if screen_name is not None: + apiURL = "http://twitter.com/friends/ids.json?screen_name=" + screen_name + "&page=" + page + try: + return simplejson.load(urllib2.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("getFriendsIDs() failed with a %s error code." % `e.code`, e.code) + + def getFollowersIDs(self, id = None, user_id = None, screen_name = None, page = "1"): + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/followers/ids/" + id + ".json" + "?page=" + page + if user_id is not None: + apiURL = "http://twitter.com/followers/ids.json?user_id=" + user_id + "&page=" + page + if screen_name is not None: + apiURL = "http://twitter.com/followers/ids.json?screen_name=" + screen_name + "&page=" + page + try: + return simplejson.load(urllib2.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("getFollowersIDs() failed with a %s error code." % `e.code`, e.code) + + def createBlock(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/blocks/create/" + id + ".json", "")) + except HTTPError, e: + raise TangoError("createBlock() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("createBlock() requires you to be authenticated.") + + def destroyBlock(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/blocks/destroy/" + id + ".json", "")) + except HTTPError, e: + raise TangoError("destroyBlock() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("destroyBlock() requires you to be authenticated.") + + def checkIfBlockExists(self, id = None, user_id = None, screen_name = None): + apiURL = "" + if id is not None: + apiURL = "http://twitter.com/blocks/exists/" + id + ".json" + if user_id is not None: + apiURL = "http://twitter.com/blocks/exists.json?user_id=" + user_id + if screen_name is not None: + apiURL = "http://twitter.com/blocks/exists.json?screen_name=" + screen_name + try: + return simplejson.load(urllib2.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("checkIfBlockExists() failed with a %s error code." % `e.code`, e.code) + + def getBlocking(self, page = "1"): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/blocks/blocking.json?page=" + page)) + except HTTPError, e: + raise TangoError("getBlocking() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getBlocking() requires you to be authenticated") + + def getBlockedIDs(self): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/blocks/blocking/ids.json")) + except HTTPError, e: + raise TangoError("getBlockedIDs() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getBlockedIDs() requires you to be authenticated.") + + def searchTwitter(self, search_query, **kwargs): + searchURL = self.constructApiURL("http://search.twitter.com/search.json", kwargs) + "&" + urllib.urlencode({"q": search_query}) + try: + return simplejson.load(urllib2.urlopen(searchURL)) + except HTTPError, e: + raise TangoError("getSearchTimeline() failed with a %s error code." % `e.code`, e.code) + + def getCurrentTrends(self, excludeHashTags = False): + apiURL = "http://search.twitter.com/trends/current.json" + if excludeHashTags is True: + apiURL += "?exclude=hashtags" + try: + return simplejson.load(urllib.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("getCurrentTrends() failed with a %s error code." % `e.code`, e.code) + + def getDailyTrends(self, date = None, exclude = False): + apiURL = "http://search.twitter.com/trends/daily.json" + questionMarkUsed = False + if date is not None: + apiURL += "?date=" + date + questionMarkUsed = True + if exclude is True: + if questionMarkUsed is True: + apiURL += "&exclude=hashtags" + else: + apiURL += "?exclude=hashtags" + try: + return simplejson.load(urllib.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("getDailyTrends() failed with a %s error code." % `e.code`, e.code) + + def getWeeklyTrends(self, date = None, exclude = False): + apiURL = "http://search.twitter.com/trends/daily.json" + questionMarkUsed = False + if date is not None: + apiURL += "?date=" + date + questionMarkUsed = True + if exclude is True: + if questionMarkUsed is True: + apiURL += "&exclude=hashtags" + else: + apiURL += "?exclude=hashtags" + try: + return simplejson.load(urllib.urlopen(apiURL)) + except HTTPError, e: + raise TangoError("getWeeklyTrends() failed with a %s error code." % `e.code`, e.code) + + def getSavedSearches(self): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/saved_searches.json")) + except HTTPError, e: + raise TangoError("getSavedSearches() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("getSavedSearches() requires you to be authenticated.") + + def showSavedSearch(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/saved_searches/show/" + id + ".json")) + except HTTPError, e: + raise TangoError("showSavedSearch() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("showSavedSearch() requires you to be authenticated.") + + def createSavedSearch(self, query): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/saved_searches/create.json?query=" + query, "")) + except HTTPError, e: + raise TangoError("createSavedSearch() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("createSavedSearch() requires you to be authenticated.") + + def destroySavedSearch(self, id): + if self.authenticated is True: + try: + return simplejson.load(self.opener.open("http://twitter.com/saved_searches/destroy/" + id + ".json", "")) + except HTTPError, e: + raise TangoError("destroySavedSearch() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("destroySavedSearch() requires you to be authenticated.") + + # The following methods are apart from the other Account methods, because they rely on a whole multipart-data posting function set. + def updateProfileBackgroundImage(self, filename, tile="true"): + if self.authenticated is True: + try: + files = [("image", filename, open(filename).read())] + fields = [] + content_type, body = self.encode_multipart_formdata(fields, files) + headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} + r = urllib2.Request("http://twitter.com/account/update_profile_background_image.json?tile=" + tile, body, headers) + return self.opener.open(r).read() + except HTTPError, e: + raise TangoError("updateProfileBackgroundImage() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("You realize you need to be authenticated to change a background image, right?") + + def updateProfileImage(self, filename): + if self.authenticated is True: + try: + files = [("image", filename, open(filename).read())] + fields = [] + content_type, body = self.encode_multipart_formdata(fields, files) + headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} + r = urllib2.Request("http://twitter.com/account/update_profile_image.json", body, headers) + return self.opener.open(r).read() + except HTTPError, e: + raise TangoError("updateProfileImage() failed with a %s error code." % `e.code`, e.code) + else: + raise TangoError("You realize you need to be authenticated to change a profile image, right?") + + def encode_multipart_formdata(self, fields, files): + BOUNDARY = mimetools.choose_boundary() + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) + L.append('Content-Type: %s' % self.get_content_type(filename)) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + def get_content_type(self, filename): + return mimetypes.guess_type(filename)[0] or 'application/octet-stream'