def validate_tweet(self, tweet):
        """
        Check to see if a Tweet is valid (i.e. we want to reply to it), and returns True if so
        Tweets from ourselves, and mentions that are not directly addressed to us, returns False
        """
        message = tweet.text

        # Bit of logging, plus we always return True for DMs
        if is_direct_message(tweet):
            logging.info("Have a DM from %s: %s", tweet.sender.screen_name, message)
            return True
        else:
            username = tweet.user.screen_name
            logging.info("Have an @ reply from %s: %s", username, message)

        # Don't start talking to yourself
        if username == self.username:
            logging.debug("Not talking to myself, that way madness lies")
            return False

        # Ignore mentions that are not direct replies
        if not message.lower().startswith('@%s' % self.username.lower()):
            logging.debug("Not a proper @ reply, skipping")
            return False

        return True
 def alert_admin_about_exception(self, tweet, exception_name):
     """
     Alert the administrator about a non-WhensMyTransportException encountered when processing a Tweet
     """
     if is_direct_message(tweet):
         tweet_time = tweet.created_at.strftime('%d-%m-%y %H:%M:%S')
         error_message = "Hey! A DM from @%s at %s GMT caused me to crash with a %s" % (tweet.sender.screen_name, tweet_time, exception_name)
     else:
         twitter_permalink = "https://twitter.com/#!/%s/status/%s" % (tweet.user.screen_name, tweet.id)
         error_message = "Hey! A tweet from @%s caused me to crash with a %s: %s" % (tweet.user.screen_name, exception_name, twitter_permalink)
     self.twitter_client.send_reply_back(error_message, self.admin_name, True)
    def check_tweets(self):
        """
        Check incoming Tweets, and reply to them
        """
        tweets = self.twitter_client.fetch_tweets()
        logging.debug("%s Tweets to process", len(tweets))
        for tweet in tweets:
            # If the Tweet is not valid (e.g. not directly addressed, from ourselves) then skip it
            if not self.validate_tweet(tweet):
                continue

            # Try processing the Tweet. This may fail with a WhensMyTransportException for a number of reasons, in which
            # case we catch the exception and process an apology accordingly. Other Python Exceptions may occur too - we handle
            # these by DMing the admin with an alert
            try:
                replies = self.process_tweet(tweet)
            except WhensMyTransportException as exc:
                replies = (exc.get_user_message(),)
            except Exception as exc:
                logging.error("Exception encountered: %s", exc.__class__.__name__)
                logging.error("Traceback:\r\n%s" % traceback.format_exc())
                self.alert_admin_about_exception(tweet, exc.__class__.__name__)
                replies = (WhensMyTransportException('unknown_error').get_user_message(),)

            # If the reply is blank, probably didn't contain a bus number or Tube line, so check to see if there was a thank-you
            if not replies:
                replies = self.check_politeness(tweet)

            # Send a reply back, if we have one. DMs and @ replies have different structures and different handlers
            for reply in replies:
                if is_direct_message(tweet):
                    self.twitter_client.send_reply_back(reply, tweet.sender.screen_name, True, tweet.id)
                else:
                    self.twitter_client.send_reply_back(reply, tweet.user.screen_name, False, tweet.id)

        self.twitter_client.check_followers()