#!/usr/bin/env python # -*- coding: utf-8 -*- import datetime import re from threading import Thread import configuration import tweepy import twitter from userio import error, ok, say import push import copy # Called when it is time to load the data at start time. def go(): load_flashes() start_streamer() # Called by the webserver to get flashes. Returns a sorted array of the # latest flashes, in order from newest to oldest. def get_latest_flashes(num): global latest_flashes, is_sorted if not is_sorted: sort_flashes() return copy.deepcopy(latest_flashes[:num]) """ Everything below this point is an artifact of Twitter as a data source. Delete it if you want, but make sure that everything above is properly implemented. """ latest_flashes = [] link_regex = re.compile(r"(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+" + r"[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(" + r"([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»\"\"'']))") rt_regex = re.compile(r"(RT @)\w+(:)") def generate_flash(text, link, source, identifier, time, channel): text = re.sub(link_regex, "", text) text = re.sub(rt_regex, "", text) text = text.strip() if isinstance(time, datetime.datetime): time = time.strftime("%a %b %d %H:%M:%S +0000 %Y") return { "text": text, # allows unicode "link": str(link), # does not allow unicode "source": str(source), # does not allow unicode "channel": str(channel), # does not allow unicode "id": str(identifier), # does not allow unicode "time": str(time) # does not allow unicode } is_sorted = False def push_flash(flash): global is_sorted latest_flashes.append(flash) is_sorted = False def sort_flashes(): global latest_flashes latest_flashes = sorted(latest_flashes, key=lambda k: int(k["id"]), reverse=True) global is_sorted is_sorted = True def load_flashes(): say("Loading latest flashes...") for accountPair in configuration.get_accounts(): say("Loading flashes from Twitter account " + accountPair[0] + " (" + accountPair[1] + ")") latest_tweets = twitter.get_latest_statuses(accountPair[0][1:]) for tweet in latest_tweets: url = None if "entities" in tweet: if "urls" in tweet['entities']: if len(tweet['entities']['urls']) > 0: url = tweet['entities']["urls"][-1]['expanded_url'] push_flash( generate_flash(tweet["text"], url, accountPair[1], tweet["id"], tweet["created_at"], accountPair[2]) ) say("Loaded " + str(len(latest_tweets)) + " flashes from " + accountPair[0]) ok("Loaded " + str(len(latest_flashes)) + " flashes") class AccountListener(tweepy.StreamListener): def on_status(self, status): try: say("Detected flash...") if configuration.is_following(status.user.screen_name): say("...from a relevant source!") url = None if 'urls' in status.entities and len(status.entities['urls']) > 0: url = status.entities['urls'][0]["expanded_url"] flash = generate_flash(status.text, url, configuration.get_name(status.user.screen_name), status.id, status.created_at, configuration.get_channel(status.user.screen_name) ) push_flash(flash) push.push_notification(flash) ok("Detected and pushed flash: " + str(flash)) else: say("...from an irrelevant source.") except Exception as e: error("Encountered an exception while processing a flash: " + str(e)) return True def on_error(self, status): error("Encountered an error while processing a status: " + str(status)) if status == 420: # returning False in on_data disconnects the stream return False return True def streamer_entrypoint(): twitter_stream = tweepy.Stream(twitter.auth, AccountListener()) twitter_stream.filter(follow=[str(twitter.get_id(k[0])) for k in configuration.get_accounts()], async=False) def start_streamer(): ok("Starting streamer...") thread = Thread(target=streamer_entrypoint) thread.setDaemon(True) thread.start() ok("Streamer started!")