diff --git a/modules/Twitter.py b/modules/Twitter.py index db497ee..8ba2be9 100644 --- a/modules/Twitter.py +++ b/modules/Twitter.py @@ -16,8 +16,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import re +from ConfigParser import NoSectionError, NoOptionError import oauth2 as oauth +import re +import sqlite3 +from threading import Timer import urlparse from extlib import irclib @@ -62,6 +65,34 @@ class Twitter(Module): self.twit = twitter.Api() self.authed = False + def db_init(self): + """Set up the settings table.""" + + # init the table if it doesn't exist + version = self.db_module_registered(self.__class__.__name__) + if version == None: + # create tables + db = self.get_db() + try: + db.execute(''' + CREATE TABLE twitter_settings ( + since_id INTEGER NOT NULL, + output_channel TEXT NOT NULL + )''') + db.execute('''INSERT INTO twitter_settings (since_id, output_channel) VALUES (0, '#drbotzo')''') + db.execute('INSERT INTO drbotzo_modules VALUES (?,?)', (self.__class__.__name__, 1)) + db.commit() + except sqlite3.Error as e: + db.rollback() + print("sqlite error: " + str(e)) + raise + + def shutdown(self): + """Deauth, and make the twitter API item inoperable.""" + + self.twit.ClearCredentials() + self.authed = False + def do(self, connection, event, nick, userhost, what, admin_unlocked): """Attempt to do twitter things.""" @@ -165,8 +196,46 @@ class Twitter(Module): # finally, create the twitter API object self.twit = twitter.Api(self.consumer_key, self.consumer_secret, self.access_token['oauth_token'], self.access_token['oauth_token_secret']) self.authed = True + + # ugly, but the best place to track it. store the connection for later use + self.connection = connection + + # print timeline stuff. this will set up the appropriate timer + self._check_self_timeline() + return 'The bot is now logged in.' + def _check_self_timeline(self): + """Check my timeline, and if there are entries, print them to the channel.""" + + if self.authed: + # get the id of the last check we made + since_id = self._get_last_since_id() + output_channel = self._get_output_channel() + + if since_id is not None and output_channel != '': + tweets = self.twit.GetFriendsTimeline(since_id=since_id) + for tweet in tweets: + tweet_text = self._return_tweet_or_retweet_text(tweet=tweet, print_source=True) + self.irc.reply(self.connection, output_channel.encode('utf-8', 'ignore'), tweet_text) + + # friends timeline printed, find the latest id + new_since_id = self._get_latest_tweet_id(tweets, since_id) + + tweets = self.twit.GetMentions(since_id=since_id) + for tweet in tweets: + tweet_text = self._return_tweet_or_retweet_text(tweet=tweet, print_source=True) + self.irc.reply(self.connection, output_channel.encode('utf-8', 'ignore'), tweet_text) + + # mentions printed, find the latest id + new_since_id = self._get_latest_tweet_id(tweets, new_since_id) + + # set since_id + self._set_last_since_id(new_since_id) + + # re-register this check + Timer(300, self._check_self_timeline, ()).start() + def _return_tweet_or_retweet_text(self, tweet, print_source=False): """ Return a string of the author and text body of a status, @@ -186,5 +255,71 @@ class Twitter(Module): else: return '%s [%s]' % (tweet.text.encode('utf-8', 'ignore'), tweet.id) + def _get_last_since_id(self): + """Get the since_id out of the database.""" + + try: + # need to create our own db object, since this is likely going + # to be called in a new thread + dbfile = self.config.get('dr.botzo', 'database') + self.conn = sqlite3.connect(dbfile) + self.conn.row_factory = sqlite3.Row + db = self.conn + query = 'SELECT since_id FROM twitter_settings' + cursor = db.execute(query) + result = cursor.fetchone() + if result: + return result['since_id'] + except sqlite3.Error as e: + print("sqlite error: " + str(e)) + raise + + def _get_output_channel(self): + """Get the output_channel out of the database.""" + + try: + # need to create our own db object, since this is likely going + # to be called in a new thread + dbfile = self.config.get('dr.botzo', 'database') + self.conn = sqlite3.connect(dbfile) + self.conn.row_factory = sqlite3.Row + db = self.conn + query = 'SELECT output_channel FROM twitter_settings' + cursor = db.execute(query) + result = cursor.fetchone() + if result: + return result['output_channel'] + except sqlite3.Error as e: + print("sqlite error: " + str(e)) + raise + + def _set_last_since_id(self, since_id): + """Set the since_id.""" + + try: + # need to create our own db object, since this is likely going + # to be called in a new thread + dbfile = self.config.get('dr.botzo', 'database') + self.conn = sqlite3.connect(dbfile) + self.conn.row_factory = sqlite3.Row + db = self.conn + cur = db.cursor() + statement = 'UPDATE twitter_settings SET since_id = ?' + cur.execute(statement, (since_id,)) + db.commit() + except sqlite3.Error as e: + print("sqlite error: " + str(e)) + raise + + def _get_latest_tweet_id(self, tweets, since_id): + """Find the latest tweet id in the provided list, or the given since_id.""" + + latest = since_id + for tweet in tweets: + if tweet.id > latest: + latest = tweet.id + + return latest + # vi:tabstop=4:expandtab:autoindent # kate: indent-mode python;indent-width 4;replace-tabs on;