Many, many changes. Broke out the url shortener so it's more plug and play; users can now throw in their own desired shortening service, and it should auto work. All timeline methods are now implemented, moving on to user methods next. Refactored some parts of the library that were becoming kludgy, and set an optional debug parameter that may be useful in the future.
This commit is contained in:
parent
60c6aae346
commit
b6a03f918c
1 changed files with 81 additions and 27 deletions
108
tango.py
108
tango.py
|
|
@ -1,7 +1,11 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Django-Twitter (Tango) utility functions. Huzzah.
|
Tango is an up-to-date library for Python that wraps the Twitter API.
|
||||||
|
Other Python Twitter libraries seem to have fallen a bit behind, and
|
||||||
|
Twitter's API has evolved a bit. Here's hoping this helps.
|
||||||
|
|
||||||
|
Questions, comments? ryan@venodesigns.net
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import urllib, urllib2
|
import urllib, urllib2
|
||||||
|
|
@ -19,12 +23,13 @@ except:
|
||||||
print "Tango requires oauth for authentication purposes. http://oauth.googlecode.com/svn/code/python/oauth/oauth.py"
|
print "Tango requires oauth for authentication purposes. http://oauth.googlecode.com/svn/code/python/oauth/oauth.py"
|
||||||
|
|
||||||
class setup:
|
class setup:
|
||||||
def __init__(self, authtype = "OAuth", username = None, password = None, oauth_keys = None):
|
def __init__(self, authtype = "OAuth", username = None, password = None, oauth_keys = None, debug = False):
|
||||||
self.authtype = authtype
|
self.authtype = authtype
|
||||||
self.authenticated = False
|
self.authenticated = False
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.oauth_keys = oauth_keys
|
self.oauth_keys = oauth_keys
|
||||||
|
self.debug = debug
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
if self.authtype == "OAuth":
|
if self.authtype == "OAuth":
|
||||||
pass
|
pass
|
||||||
|
|
@ -37,9 +42,26 @@ class setup:
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def shortenURL(self, url_to_shorten):
|
def explainOAuthSupport(self):
|
||||||
# Perhaps we should have fallbacks here in case the is.gd API limit gets hit? Maybe allow them to set the host?
|
print "Sorry, OAuth support is still forthcoming. Default back to Basic Authentication for now, or help out on this front!"
|
||||||
shortURL = urllib2.urlopen("http://is.gd/api.php?" + urllib.urlencode({"longurl": url_to_shorten})).read()
|
pass
|
||||||
|
|
||||||
|
# OAuth functions; shortcuts for verifying the credentials.
|
||||||
|
def fetch_response_oauth(self, oauth_request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_unauthorized_request_token(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_authorization_url(self, token):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exchange_tokens(self, request_token):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# URL Shortening function huzzah
|
||||||
|
def shortenURL(self, url_to_shorten, shortener = "http://is.gd/api.php", query = "longurl"):
|
||||||
|
shortURL = urllib2.urlopen(shortener + "?" + urllib.urlencode({query: url_to_shorten})).read()
|
||||||
return shortURL
|
return shortURL
|
||||||
|
|
||||||
def constructApiURL(self, base_url, params):
|
def constructApiURL(self, base_url, params):
|
||||||
|
|
@ -51,28 +73,45 @@ class setup:
|
||||||
questionMarkUsed = True
|
questionMarkUsed = True
|
||||||
return queryURL
|
return queryURL
|
||||||
|
|
||||||
|
def createGenericTimeline(self, existingTimeline):
|
||||||
|
# Many of Twitter's API functions return the same style of data. This function just wraps it if we need it.
|
||||||
|
genericTimeline = []
|
||||||
|
for tweet in existingTimeline:
|
||||||
|
genericTimeline.append({
|
||||||
|
"created_at": tweet["created_at"],
|
||||||
|
"in_reply_to_screen_name": tweet["in_reply_to_screen_name"],
|
||||||
|
"in_reply_to_status_id": tweet["in_reply_to_status_id"],
|
||||||
|
"in_reply_to_user_id": tweet["in_reply_to_user_id"],
|
||||||
|
"id": tweet["id"],
|
||||||
|
"text": tweet["text"]
|
||||||
|
})
|
||||||
|
return genericTimeline
|
||||||
|
|
||||||
def getPublicTimeline(self):
|
def getPublicTimeline(self):
|
||||||
publicTimeline = simplejson.load(urllib2.urlopen("http://twitter.com/statuses/public_timeline.json"))
|
return self.createGenericTimeline(simplejson.load(urllib2.urlopen("http://twitter.com/statuses/public_timeline.json")))
|
||||||
formattedTimeline = []
|
|
||||||
for tweet in publicTimeline:
|
def getFriendsTimeline(self, **kwargs):
|
||||||
formattedTimeline.append(tweet['text'])
|
if self.authenticated is True:
|
||||||
return formattedTimeline
|
friendsTimelineURL = self.constructApiURL("http://twitter.com/statuses/friends_timeline.json", kwargs)
|
||||||
|
return self.createGenericTimeline(simplejson.load(self.opener.open(friendsTimelineURL)))
|
||||||
|
else:
|
||||||
|
print "getFriendsTimeline() requires you to be authenticated."
|
||||||
|
pass
|
||||||
|
|
||||||
def getUserTimeline(self, **kwargs):
|
def getUserTimeline(self, **kwargs):
|
||||||
# 99% API compliant, I think - need to figure out Gzip compression and auto-getting based on authentication
|
|
||||||
# By doing this with kwargs and constructing a url outside, we can stay somewhat agnostic of API changes - it's all
|
|
||||||
# based on what the user decides to pass. We just handle the heavy lifting! :D
|
|
||||||
userTimelineURL = self.constructApiURL("http://twitter.com/statuses/user_timeline/" + self.username + ".json", kwargs)
|
userTimelineURL = self.constructApiURL("http://twitter.com/statuses/user_timeline/" + self.username + ".json", kwargs)
|
||||||
userTimeline = simplejson.load(urllib2.urlopen(userTimelineURL))
|
try:
|
||||||
formattedTimeline = []
|
return self.createGenericTimeline(simplejson.load(urllib2.urlopen(userTimelineURL)))
|
||||||
for tweet in userTimeline:
|
except:
|
||||||
formattedTimeline.append(tweet['text'])
|
print "Hmmm, that failed. Does this user hide/protect their updates? If so, you'll need to authenticate and be their friend to get their timeline."
|
||||||
return formattedTimeline
|
pass
|
||||||
|
|
||||||
def getUserMentions(self, **kwargs):
|
def getUserMentions(self, **kwargs):
|
||||||
if self.authenticated is True:
|
if self.authenticated is True:
|
||||||
if self.authtype == "Basic":
|
if self.authtype == "Basic":
|
||||||
pass
|
mentionsFeedURL = self.constructApiURL("http://twitter.com/statuses/mentions.json", kwargs)
|
||||||
|
mentionsFeed = simplejson.load(self.opener.open(mentionsFeedURL))
|
||||||
|
return self.createGenericTimeline(mentionsFeed)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
|
@ -88,26 +127,41 @@ class setup:
|
||||||
print e.code
|
print e.code
|
||||||
print e.headers
|
print e.headers
|
||||||
else:
|
else:
|
||||||
print "Sorry, OAuth support is still forthcoming. Feel free to help out on this front!"
|
self.explainOAuthSupport()
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
print "updateStatus() requires you to be authenticated."
|
print "updateStatus() requires you to be authenticated."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def destroyStatus(self, id):
|
def destroyStatus(self, id):
|
||||||
if self.authenticated is True:
|
if self.authenticated is True:
|
||||||
self.http.request("http://twitter.com/status/destroy/" + id + ".json", "POST")
|
if self.authtype == "Basic":
|
||||||
|
self.opener.open("http://twitter.com/status/destroy/" + id + ".json", "POST")
|
||||||
|
else:
|
||||||
|
self.explainOAuthSupport()
|
||||||
else:
|
else:
|
||||||
print "destroyStatus() requires you to be authenticated."
|
print "destroyStatus() requires you to be authenticated."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def getSearchTimeline(self, search_query, optional_page):
|
def getSearchTimeline(self, search_query, optional_page):
|
||||||
params = urllib.urlencode({'q': search_query, 'rpp': optional_page}) # Doesn't hurt to do pages this way. *shrug*
|
params = urllib.urlencode({'q': search_query, 'rpp': optional_page}) # Doesn't hurt to do pages this way. *shrug*
|
||||||
searchTimeline = simplejson.load(urllib2.urlopen("http://search.twitter.com/search.json", params))
|
try:
|
||||||
formattedTimeline = []
|
searchTimeline = simplejson.load(urllib2.urlopen("http://search.twitter.com/search.json", params))
|
||||||
for tweet in searchTimeline['results']:
|
# This one is custom built because the search feed is a bit different than the other feeds.
|
||||||
formattedTimeline.append(tweet['text'])
|
genericTimeline = []
|
||||||
return formattedTimeline
|
for tweet in searchTimeline["results"]:
|
||||||
|
genericTimeline.append({
|
||||||
|
"created_at": tweet["created_at"],
|
||||||
|
"from_user": tweet["from_user"],
|
||||||
|
"from_user_id": tweet["from_user_id"],
|
||||||
|
"profile_image_url": tweet["profile_image_url"],
|
||||||
|
"id": tweet["id"],
|
||||||
|
"text": tweet["text"],
|
||||||
|
"to_user_id": tweet["to_user_id"]
|
||||||
|
})
|
||||||
|
return genericTimeline
|
||||||
|
except HTTPError, e:
|
||||||
|
print e.code
|
||||||
|
print e.headers
|
||||||
|
|
||||||
def getCurrentTrends(self):
|
def getCurrentTrends(self):
|
||||||
# Returns an array of dictionary items containing the current trends
|
# Returns an array of dictionary items containing the current trends
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue