Permalink
Browse files

Add a script that synchronizes a twitter list with the list of subscr…

…ibers.

In passing, create a TwitterClient base class for use for all communication
with twitter.
  • Loading branch information...
1 parent 5698f2c commit c603862e65e094723febe7347a77ce4f50dfc51b @mhagander committed Aug 28, 2010
Showing with 111 additions and 0 deletions.
  1. +41 −0 synctwitter.py
  2. +70 −0 twitterclient.py
View
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# vim: ai ts=4 sts=4 sw=4
+"""PostgreSQL Planet Aggregator
+
+This file contains the functions to synchronize the list
+of twitter handles with a list on the twitter account.
+
+Copyright (C) 2009-2010 PostgreSQL Global Development Group
+"""
+
+import psycopg2
+import psycopg2.extensions
+import ConfigParser
+from twitterclient import TwitterClient
+
+class SyncTwitter(TwitterClient):
+ def __init__(self, cfg):
+ TwitterClient.__init__(self, cfg)
+
+ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
+ self.db = psycopg2.connect(c.get('planet','db'))
+
+ def Run(self):
+ # Get list of handles that should be on the list
+ curs = self.db.cursor()
+ curs.execute("SELECT DISTINCT twitteruser FROM planet.feeds WHERE NOT (twitteruser IS NULL OR twitteruser='') ORDER BY twitteruser");
+ expected = set([r[0] for r in curs.fetchall()])
+
+ # Get list of current screen names the list is following
+ response = self.twitter_request('subscribers/members.json')
+ current = set([x['screen_name'] for x in response['users']])
+
+ # Start by deleting, then adding the new ones
+ map(self.remove_subscriber, current.difference(expected))
+ map(self.add_subscriber, expected.difference(current))
+
+
+if __name__=="__main__":
+ c = ConfigParser.ConfigParser()
+ c.read('planet.ini')
+ SyncTwitter(c).Run()
View
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# vim: ai ts=4 sts=4 sw=4
+"""PostgreSQL Planet Aggregator
+
+This file contains a base class for twitter integration
+scripts.
+
+Copyright (C) 2009-2010 PostgreSQL Global Development Group
+"""
+import oauth2 as oauth
+import simplejson as json
+import time
+import urllib
+
+class TwitterClient(object):
+ """
+ Base class representing a twitter client, implementing all those twitter
+ API calls that are in use.
+ Does not attempt to be a complete twitter client, just to fill the needs
+ for the planet software.
+ """
+
+ def __init__(self, cfg):
+ """
+ Initialize the instance. The parameter cfg is a ConfigParser object
+ that has loaded the planet.ini file.
+ """
+ self.twittername = cfg.get('twitter', 'account')
+ self.oauth_token = oauth.Token(cfg.get('twitter', 'token'), cfg.get('twitter', 'secret'))
+ self.oauth_consumer = oauth.Consumer(cfg.get('twitter', 'consumer'), cfg.get('twitter', 'consumersecret'))
+
+ def twitter_request(self, apicall, method='GET', ext_params=None):
+ params = {
+ 'oauth_version': "1.0",
+ 'oauth_nonce': oauth.generate_nonce(),
+ 'oauth_timestamp': int(time.time()),
+ 'oauth_token': self.oauth_token.key,
+ 'oauth_consumer_key': self.oauth_consumer.key,
+ }
+ if ext_params:
+ params.update(ext_params)
+
+ url = "https://api.twitter.com/1/%s/%s" % (self.twittername, apicall)
+
+ req = oauth.Request(method=method,
+ url=url,
+ parameters=params)
+ req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.oauth_consumer, self.oauth_token)
+ if method=='GET':
+ instream = urllib.urlopen(req.to_url())
+ else:
+ instream=urllib.urlopen(url, req.to_postdata())
+
+ # Make the actual call to twitter
+ ret=instream.read()
+ instream.close()
+ return json.loads(ret)
+
+ def remove_subscriber(self, name):
+ print "Removing twitter user %s from list." % name
+ self.twitter_request('subscribers/members.json', 'POST', {
+ 'id': name,
+ '_method': 'DELETE',
+ })
+
+ def add_subscriber(self, name):
+ print "Adding twitter user %s to list." % name
+ self.twitter_request('subscribers/members.json', 'POST', {
+ 'id': name,
+ })

0 comments on commit c603862

Please sign in to comment.