From 4882115fff0b96fda0b837d741bc3721aacce50a Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sun, 1 Jan 2017 20:00:12 +0100 Subject: Saved a first version of HTT. --- config.py | 12 ++++++ db.py | 84 ++++++++++++++++++++++++++++++++++++++++++ htt.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 config.py create mode 100644 db.py create mode 100755 htt.py diff --git a/config.py b/config.py new file mode 100644 index 0000000..b1a3471 --- /dev/null +++ b/config.py @@ -0,0 +1,12 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +# List of space-separated hashtags to follow, for instance #python #bot +hashtags = '#python #bot' + +# Keywords to find in Tweets we want to highlight +white_kwds = 'you got the idea' + +# Age of old Tweets to get purged in days +max_age = 14 diff --git a/db.py b/db.py new file mode 100644 index 0000000..ad497a0 --- /dev/null +++ b/db.py @@ -0,0 +1,84 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +from config import max_age +import sqlite3 +import time +import tweepy + + +class LikeMemory(): + """Track all liked Tweets.""" + + def __init__(self, api): + """Build the Python object.""" + + self._api = api + + self._db = sqlite3.connect('HTT.db', detect_types=sqlite3.PARSE_DECLTYPES) + + sqlite3.register_adapter(bool, int) + sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v))) + + sql = ''' + CREATE TABLE IF NOT EXISTS LikedTweets( + sid INTEGER PRIMARY KEY, + username TEXT, + timestamp INTEGER, + purged BOOLEAN + ) + ''' + + cursor = self._db.cursor() + cursor.execute(sql) + self._db.commit() + + + def save_liked_status(self, sid, username): + """Remember a given liked status.""" + + timestamp = int(time.time()) + + values = (sid, username, timestamp, False) + + cursor = self._db.cursor() + cursor.execute('INSERT INTO LikedTweets VALUES (?, ?, ?, ?)', values) + self._db.commit() + + print(timestamp) + + + def purge_old_status(self): + """Purge old seen statuses.""" + + timestamp = int(time.time()) - max_age * 24 * 60 * 60 + + values = (timestamp, False) + + cursor = self._db.cursor() + cursor.execute('SELECT sid FROM LikedTweets WHERE timestamp < ? AND purged = ?', values) + + rows = cursor.fetchall() + + for row in rows: + + sid = row[0] + + try: + + self._api.destroy_favorite(sid) + + # tweepy.error.TweepError: [{'code': 144, 'message': 'No status found with that ID.'}] + except tweepy.error.TweepError as err: + + pass + + values = (True, sid) + + cursor = self._db.cursor() + cursor.execute('UPDATE LikedTweets SET purged = ? WHERE sid = ?', values) + + self._db.commit() + + print('Purged %d liked Tweet%s!' % (len(rows), '' if len(rows) <= 1 else 's')) diff --git a/htt.py b/htt.py new file mode 100755 index 0000000..cc82531 --- /dev/null +++ b/htt.py @@ -0,0 +1,123 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +import tweepy +from tweepy import OAuthHandler +from tweepy import Stream +from tweepy.streaming import StreamListener +from auth import * +from config import hashtags, white_kwds +from db import LikeMemory +import json +import sys + + +class StdOutListener(StreamListener): + """A listener handles tweets are the received from the stream.""" + + def __init__(self, api, memory): + """Build the Python object.""" + + super().__init__() + + self._api = api + self._memory = memory + + self._white = [ s.lower() for s in white_kwds.split(' ') ] + + + def get_status_info(self, data): + """Parse status data to get information about its author and content.""" + + # Do not rely on https://dev.twitter.com/overview/api/tweets + # as the specs seem outdated... + + sid = data['id'] + username = data['user']['screen_name'] + + if 'extended_tweet' in data: + content = data['extended_tweet']['full_text'] + else: + content = data['text'] + + content = content.replace('\n', '') + + return sid, username, content + + + def on_data(self, data): + """Receive Tweets matching the given hashtags.""" + + decoded = json.loads(data) + + if 'retweeted_status' in decoded: + sid, username, content = self.get_status_info(decoded['retweeted_status']) + else: + sid, username, content = self.get_status_info(decoded) + + like = False + + words = content.split(' ') + + for kwd in self._white: + + for w in words: + if w.lower() == kwd: + like = True + break + + if like: + break + + if like: + + try: + + self._api.create_favorite(sid) + + self._memory.save_liked_status(sid, username) + + print('@%s: "%s" (id=%d)' % (username, content, sid)) + print(' -> https://twitter.com/%s/status/%d' % (username, sid)) + + except tweepy.error.TweepError: + + pass + + else: + + print('Reject "%s"' % content) + + return True + + + def on_error(self, status): + """Handle errors.""" + + print('Error:', status) + + if status_code == 420: + #returning False in on_data disconnects the stream + return False + + +if __name__ == '__main__': + """Start of the script.""" + + auth = OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) + auth.set_access_token(ACCESS_KEY, ACCESS_SECRET) + + api = tweepy.API(auth) + memory = LikeMemory(api) + + if len(sys.argv) > 1 and sys.argv[1] == '--purge': + + memory.purge_old_status() + + else: + + listener = StdOutListener(api, memory) + + stream = Stream(auth, listener) + stream.filter(track=hashtags.split(' ')) -- cgit v0.11.2-87-g4458