#!/usr/bin/env python3 from config import config import logging import tweepy import re import requests from time import sleep import report from user import User logger = logging.getLogger(__name__) class TwitterBot(object): """ This bot retweets all tweets which 1) mention him, 2) contain at least one of the triggerwords provided. api: The api object, generated with your oAuth keys, responsible for communication with twitter rest API last_mention: the ID of the last tweet which mentioned you """ def __init__(self, uid): """ Initializes the bot and loads all the necessary data. Tweet """ self.user = User(uid) # initialize API access keys = self.get_api_keys() auth = tweepy.OAuthHandler(consumer_key=keys[0], consumer_secret=keys[1]) auth.set_access_token(keys[2], # access_token_key keys[3]) # access_token_secret self.api = tweepy.API(auth) self.last_mention = self.user.get_seen_tweet() self.waitcounter = 0 def get_api_keys(self): """ How to get these keys is described in doc/twitter_api.md After you received keys, store them in your config.toml like this: [tapp] consumer_key = "..." consumer_secret = "..." [tuser] access_token_key = "..." access_token_secret = "..." :return: keys: list of these 4 strings. """ keys = [config['twitter']['consumer_key'], config['twitter']['consumer_secret']] row = self.user.get_twitter_token() keys.append(row[0]) keys.append(row[1]) return keys def save_last(self): """ Saves the last retweeted tweet in last_mention. """ self.user.save_seen_tweet(self.last_mention) def waiting(self): """ If the counter is not 0, you should be waiting instead. :return: self.waitcounter(int): if 0, do smth. """ if self.waitcounter > 0: sleep(1) self.waitcounter -= 1 return self.waitcounter def crawl(self): """ crawls all Tweets which mention the bot from the twitter rest API. :return: reports: (list of report.Report objects) """ reports = [] try: if not self.waiting(): if self.last_mention == 0: mentions = self.api.mentions_timeline() else: mentions = self.api.mentions_timeline( since_id=self.last_mention) for status in mentions: text = re.sub( "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", "", status.text) reports.append(report.Report(status.author.screen_name, "twitter", text, status.id, status.created_at)) self.save_last() return reports except tweepy.RateLimitError: logger.error("Twitter API Error: Rate Limit Exceeded", exc_info=True) self.waitcounter += 60*15 + 1 except requests.exceptions.ConnectionError: logger.error("Twitter API Error: Bad Connection", exc_info=True) self.waitcounter += 10 except tweepy.TweepError: logger.error("Twitter API Error: General Error", exc_info=True) return [] def repost(self, status): """ Retweets a given tweet. :param status: (report.Report object) :return: toot: string of the tweet, to toot on mastodon. """ while 1: try: self.api.retweet(status.id) logger.info("Retweeted: " + status.format()) if status.id > self.last_mention: self.last_mention = status.id self.save_last() return status.format() except requests.exceptions.ConnectionError: logger.error("Twitter API Error: Bad Connection", exc_info=True) sleep(10) # maybe one day we get rid of this error: except tweepy.TweepError: logger.error("Twitter Error", exc_info=True) if status.id > self.last_mention: self.last_mention = status.id self.save_last() return None def post(self, status): """ Tweet a post. :param status: (report.Report object) """ text = status.format() if len(text) > 280: text = status.text[:280 - 4] + u' ...' while 1: try: self.api.update_status(status=text) return except requests.exceptions.ConnectionError: logger.error("Twitter API Error: Bad Connection", exc_info=True) sleep(10) def flow(self, trigger, to_tweet=()): """ The flow of crawling mentions and retweeting them. :param to_tweet: list of strings to tweet :return list of retweeted tweets, to toot on mastodon """ # Tweet the reports from other sources for post in to_tweet: self.post(post) # Store all mentions in a list of Status Objects mentions = self.crawl() # initialise list of strings for other bots all_tweets = [] for status in mentions: # Is the Text of the Tweet in the triggerlist? if trigger.is_ok(status.text): # Retweet status toot = self.repost(status) if toot: all_tweets.append(toot) # Return Retweets for posting on other bots return all_tweets