Add a script that synchronizes a twitter list with the list of subscribers.
authorMagnus Hagander <magnus@hagander.net>
Sat, 28 Aug 2010 11:53:20 +0000 (13:53 +0200)
committerMagnus Hagander <magnus@hagander.net>
Sat, 28 Aug 2010 11:53:20 +0000 (13:53 +0200)
In passing, create a TwitterClient base class for use for all communication
with twitter.

synctwitter.py [new file with mode: 0755]
twitterclient.py [new file with mode: 0644]

diff --git a/synctwitter.py b/synctwitter.py
new file mode 100755 (executable)
index 0000000..b2a976d
--- /dev/null
@@ -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()
diff --git a/twitterclient.py b/twitterclient.py
new file mode 100644 (file)
index 0000000..bf7c009
--- /dev/null
@@ -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,
+                               })