reworked twitterbot according to new scheme
This commit is contained in:
parent
49bd00fba3
commit
66bb1f86a3
|
@ -1,190 +1,71 @@
|
|||
#!/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
|
||||
from bot import Bot
|
||||
|
||||
|
||||
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()
|
||||
class TwitterBot(Bot):
|
||||
def get_api(self, user):
|
||||
keys = user.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)
|
||||
return 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):
|
||||
def crawl(self, user):
|
||||
"""
|
||||
crawls all Tweets which mention the bot from the twitter rest API.
|
||||
|
||||
:return: reports: (list of report.Report objects)
|
||||
"""
|
||||
reports = []
|
||||
api = self.get_api(user)
|
||||
last_mention = user.get_seen_tweet()
|
||||
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
|
||||
if last_mention == 0:
|
||||
mentions = api.mentions_timeline()
|
||||
else:
|
||||
mentions = api.mentions_timeline(
|
||||
since_id=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))
|
||||
user.save_seen_tweet(last_mention)
|
||||
return reports
|
||||
except tweepy.RateLimitError:
|
||||
logger.error("Twitter API Error: Rate Limit Exceeded",
|
||||
exc_info=True)
|
||||
self.waitcounter += 60*15 + 1
|
||||
# :todo implement rate limiting
|
||||
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
|
||||
def post(self, user, report):
|
||||
api = self.get_api(user)
|
||||
try:
|
||||
if report.source == self:
|
||||
api.retweet(report.id)
|
||||
else:
|
||||
# text = report.format()
|
||||
if len(report.text) > 280:
|
||||
text = report.text[:280 - 4] + u' ...'
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.error("Twitter API Error: Bad Connection",
|
||||
exc_info=True)
|
||||
# :todo implement rate limiting
|
||||
|
|
|
@ -10,9 +10,6 @@ import smtplib
|
|||
from mastodon import Mastodon
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@get('/')
|
||||
@view('template/propaganda.tpl')
|
||||
def propaganda():
|
||||
|
@ -153,6 +150,12 @@ def login_mastodon(user):
|
|||
return dict(error='Login to Mastodon failed.')
|
||||
|
||||
|
||||
logpath = config['logging']['logpath']
|
||||
logger = logging.getLogger()
|
||||
fh = logging.FileHandler(logpath)
|
||||
fh.setLevel(logging.DEBUG)
|
||||
logger.addHandler(fh)
|
||||
|
||||
application = bottle.default_app()
|
||||
bottle.install(SessionPlugin('/'))
|
||||
|
||||
|
|
9
user.py
9
user.py
|
@ -1,3 +1,4 @@
|
|||
from config import config
|
||||
from bottle import response
|
||||
from db import db
|
||||
import jwt
|
||||
|
@ -81,6 +82,14 @@ class User(object):
|
|||
instance = db.cur.fetchone()
|
||||
return instance[1], instance[2], row[0], instance[0]
|
||||
|
||||
def get_twitter_credentials(self):
|
||||
keys = [config['twitter']['consumer_key'],
|
||||
config['twitter']['consumer_secret']]
|
||||
row = self.get_twitter_token()
|
||||
keys.append(row[0])
|
||||
keys.append(row[1])
|
||||
return keys
|
||||
|
||||
def get_seen_toot(self):
|
||||
db.execute("SELECT toot_id FROM seen_toots WHERE user_id = ?;",
|
||||
(self.uid, ))
|
||||
|
|
Loading…
Reference in a new issue