Merge branch 'multi-deployment' of https://github.com/b3yond/ticketfrei into multi-deployment

master
Tech 2018-03-28 23:33:43 +02:00
commit 80415cd093
11 changed files with 171 additions and 182 deletions

View File

@ -20,14 +20,13 @@ class Mailbot(object):
other bots that it received mails.
"""
def __init__(self, uid, db):
def __init__(self, uid):
"""
Creates a Bot who listens to mails and forwards them to other
bots.
:param config: (dictionary) config.toml as a dictionary of dictionaries
"""
self.user = User(db, uid)
self.user = User(uid)
try:
self.last_mail = self.user.get_seen_mail()
@ -115,7 +114,7 @@ class Mailbot(object):
:param status: (report.Report object)
"""
mailer = sendmail.Mailer(config)
mailer = sendmail.Mailer()
mailer.send(status.format(), self.mailinglist,
"Warnung: Kontrolleure gesehen")

View File

@ -26,10 +26,9 @@ class MastodonBot(Bot):
return mentions
for status in notifications:
if (status['type'] == 'mention' and
status['status']['id'] > self.seen_toots):
status['status']['id'] > user.get_seen_toot()):
# save state
self.seen_toots = status['status']['id']
self.save_last()
user.save_seen_toot(status['status']['id'])
# add mention to mentions
text = re.sub(r'<[^>]*>', '', status['status']['content'])
text = re.sub(

View File

@ -1,193 +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, db):
"""
Initializes the bot and loads all the necessary data.
:param config: (dictionary) config.toml as a dictionary of dictionaries
:param history_path: Path to the file with ID of the last retweeted
Tweet
"""
self.db = db
self.user = User(db, 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

View File

@ -33,7 +33,7 @@ if __name__ == '__main__':
time.sleep(60) # twitter rate limit >.<
except:
logger.error('Shutdown', exc_info=True)
mailer = sendmail.Mailer(config)
mailer = sendmail.Mailer()
try:
mailer.send('', config['web']['contact'],
'Ticketfrei Crash Report',

4
bot.py
View File

@ -1,8 +1,8 @@
class Bot(object):
# returns a list of Report objects
def crawl(user):
def crawl(self, user):
pass
# post/boost Report object
def post(user, report):
def post(self, user, report):
pass

View File

@ -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('/'))

104
mall bugfixes Normal file
View File

@ -0,0 +1,104 @@
diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py
index a98872a..a11231f 100644
--- a/active_bots/mailbot.py
+++ b/active_bots/mailbot.py
@@ -20,14 +20,13 @@ class Mailbot(object):
other bots that it received mails.
"""

- def __init__(self, uid, db):
+ def __init__(self, uid):
"""
Creates a Bot who listens to mails and forwards them to other
bots.

- :param config: (dictionary) config.toml as a dictionary of dictionaries
"""
- self.user = User(db, uid)
+ self.user = User(uid)

try:
self.last_mail = self.user.get_seen_mail()
@@ -115,7 +114,7 @@ class Mailbot(object):

:param status: (report.Report object)
"""
- mailer = sendmail.Mailer(config)
+ mailer = sendmail.Mailer()
mailer.send(status.format(), self.mailinglist,
"Warnung: Kontrolleure gesehen")

diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py
index 787cdfb..065da45 100755
--- a/active_bots/twitterbot.py
+++ b/active_bots/twitterbot.py
@@ -24,16 +24,13 @@ class TwitterBot(object):
last_mention: the ID of the last tweet which mentioned you
"""

- def __init__(self, uid, db):
+ def __init__(self, uid):
"""
Initializes the bot and loads all the necessary data.

- :param config: (dictionary) config.toml as a dictionary of dictionaries
- :param history_path: Path to the file with ID of the last retweeted
Tweet
"""
- self.db = db
- self.user = User(db, uid)
+ self.user = User(uid)

# initialize API access
keys = self.get_api_keys()
diff --git a/sendmail.py b/sendmail.py
index df91d1d..93028d9 100755
--- a/sendmail.py
+++ b/sendmail.py
@@ -2,6 +2,7 @@

import smtplib
import ssl
+from config import config
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
@@ -12,13 +13,12 @@ class Mailer(object):
Maintains the connection to the mailserver and sends text to users.
"""

- def __init__(self, config):
+ def __init__(self):
"""
Creates an SMTP client to send a mail. Is called only once
when you actually want to send a mail. After you sent the
mail, the SMTP client is shut down again.

- :param config: The config file generated from config.toml
"""
# This generates the From address by stripping the part until the first
# period from the mail server address and won't work always.
@@ -65,9 +65,5 @@ class Mailer(object):

# For testing:
if __name__ == '__main__':
- import prepare
-
- config = prepare.get_config()
-
- m = Mailer(config)
+ m = Mailer()
print(m.send("This is a test mail.", m.fromaddr, "Test"))
diff --git a/user.py b/user.py
index c4e99e4..ce95cd3 100644
--- a/user.py
+++ b/user.py
@@ -53,7 +53,7 @@ class User(object):
return jwt.encode({
'email': email,
'uid': self.uid
- }, self.secret).decode('ascii')
+ }, db.secret).decode('ascii')

def is_appropriate(self, report):
db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;",

View File

@ -8,6 +8,7 @@ Students: usually already have a ticket, but may be solidaric
Leftist scene
* Flyers in alternative centers
* Graffitis in alternative neighbourhoods
* Posters in social centers
Schools:
* especially trade schools

View File

@ -2,6 +2,7 @@
import smtplib
import ssl
from config import config
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
@ -12,13 +13,12 @@ class Mailer(object):
Maintains the connection to the mailserver and sends text to users.
"""
def __init__(self, config):
def __init__(self):
"""
Creates an SMTP client to send a mail. Is called only once
when you actually want to send a mail. After you sent the
mail, the SMTP client is shut down again.
:param config: The config file generated from config.toml
"""
# This generates the From address by stripping the part until the first
# period from the mail server address and won't work always.
@ -65,9 +65,5 @@ class Mailer(object):
# For testing:
if __name__ == '__main__':
import prepare
config = prepare.get_config()
m = Mailer(config)
m = Mailer()
print(m.send("This is a test mail.", m.fromaddr, "Test"))

View File

@ -20,7 +20,7 @@ class SessionPlugin(object):
uid = request.get_cookie('uid', secret=db.secret)
if uid is None:
return redirect(self.loginpage)
kwargs[self.keyword] = User(db, uid)
kwargs[self.keyword] = User(uid)
return callback(*args, **kwargs)
return wrapper

11
user.py
View File

@ -1,3 +1,4 @@
from config import config
from bottle import response
from db import db
import jwt
@ -53,7 +54,7 @@ class User(object):
return jwt.encode({
'email': email,
'uid': self.uid
}, self.secret).decode('ascii')
}, db.secret).decode('ascii')
def is_appropriate(self, report):
db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;",
@ -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, ))