reworked mastodon to work with frontend. user, config, logging -> new files.

master
b3yond 2018-03-23 15:51:52 +01:00
parent e6c5d0d745
commit 878a578dec
7 changed files with 188 additions and 169 deletions

View File

@ -1,73 +1,76 @@
#!/usr/bin/env python3
import logging
import pytoml as toml
import prepare
import time
import sendmail
from db import DB
from retootbot import RetootBot
from retweetbot import RetweetBot
from mailbot import Mailbot
# from retweetbot import RetweetBot
# from mailbot import Mailbot
from trigger import Trigger
def get_logger(config):
logpath = config['logging']['logpath']
logger = logging.getLogger()
fh = logging.FileHandler(logpath)
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
return logger
def get_config():
# read config in TOML format (https://github.com/toml-lang/toml#toml)
with open('config.toml') as configfile:
config = toml.load(configfile)
return config
def get_users(db):
user_rows = db.get_users()
users = {}
for row in user_rows:
users[row[0]] = []
return users
def init_bots(config, logger, db, users):
for uid in users:
users[uid].append(RetootBot(config, logger, uid, db))
# users[uid].append(RetweetBot(config, uid, db))
# users[uid].append(Mailbot(config, uid, db))
return users
def run():
config = get_config()
logger = get_logger(config)
config = prepare.get_config()
logger = prepare.get_logger(config)
db = DB()
# set trigger
trigger = Trigger(config)
# initialize bots
bots = []
if config["muser"]["enabled"] != "false":
bots.append(RetootBot(config))
if config["tuser"]["enabled"] != "false":
bots.append(RetweetBot(config))
if config["mail"]["enabled"] != "false":
bots.append(Mailbot(config))
while True:
# get a dictionary { uid : [ Bot objects ] }
users = get_users(db)
# initialize bots
users = init_bots(config, logger, db, users)
try:
statuses = []
while True:
for bot in bots:
reports = bot.crawl()
for status in reports:
if not trigger.is_ok(status.text):
continue
for bot2 in bots:
if bot == bot2:
bot2.repost(status)
else:
bot2.post(status)
time.sleep(60) # twitter rate limit >.<
except KeyboardInterrupt:
print("Good bye. Remember to restart the bot!")
except:
logger.error('Shutdown', exc_info=True)
for bot in bots:
bot.save_last()
mailer = sendmail.Mailer(config)
try:
mailer.send('', config['mail']['contact'],
'Ticketfrei Crash Report',
attachment=config['logging']['logpath'])
for uid in users:
for bot in users[uid]:
reports = bot.crawl()
for status in reports:
if not trigger.is_ok(status.text):
continue
for bot2 in users[uid]:
if bot == bot2:
bot2.repost(status)
else:
bot2.post(status)
time.sleep(60) # twitter rate limit >.<
except KeyboardInterrupt:
print("Good bye. Remember to restart the bot!")
except:
logger.error('Mail sending failed', exc_info=True)
logger.error('Shutdown', exc_info=True)
for uid in users:
for bot in users[uid]:
bot.save_last()
mailer = sendmail.Mailer(config)
try:
mailer.send('', config['mail']['contact'],
'Ticketfrei Crash Report',
attachment=config['logging']['logpath'])
except:
logger.error('Mail sending failed', exc_info=True)
if __name__ == '__main__':
run()
run()

63
db.py
View File

@ -1,18 +1,18 @@
from bottle import redirect, request, response
from bottle import redirect, request
from functools import wraps
from inspect import Signature
import jwt
from os import path, urandom
from pylibscrypt import scrypt_mcf, scrypt_mcf_check
import sqlite3
import backend
from mastodon import Mastodon
import prepare
from user import User
class DB(object):
def __init__(self):
self.config = backend.get_config()
self.logger = backend.get_logger(self.config)
self.config = prepare.get_config()
self.logger = prepare.get_logger(self.config)
dbfile = path.join(path.dirname(path.abspath(__file__)),
'ticketfrei.sqlite')
self.conn = sqlite3.connect(dbfile)
@ -132,56 +132,9 @@ class DB(object):
def close(self):
self.conn.close()
class User(object):
def __init__(self, db, uid):
# set cookie
response.set_cookie('uid', uid, secret=db.secret, path='/')
self.db = db
self.uid = uid
def state(self):
return dict(foo='bar')
def save_request_token(self, token):
self.db.cur.execute("INSERT INTO twitter_request_tokens(user_id, request_token) VALUES(?, ?);",
(self.uid, token))
self.db.conn.commit()
def get_request_token(self):
self.db.cur.execute("SELECT request_token FROM twitter_request_tokens WHERE user_id = ?;", (id,))
request_token = self.db.cur.fetchone()[0]
self.db.cur.execute("DELETE FROM twitter_request_tokens WHERE user_id = ?;", (id,))
self.db.conn.commit()
return request_token
def save_twitter_token(self, access_token, access_token_secret):
self.db.cur.execute(
"INSERT INTO twitter_accounts(user_id, access_token_key, access_token_secret) VALUES(?, ?, ?);",
(id, access_token, access_token_secret))
self.db.conn.commit()
def get_mastodon_app_keys(self, instance):
self.db.cur.execute("SELECT client_id, client_secret FROM mastodon_instances WHERE instance = ?;", (instance, ))
try:
row = self.db.cur.fetchone()
client_id = row[0]
client_secret = row[1]
return client_id, client_secret
except TypeError:
app_name = "ticketfrei" + str(self.db.secret)[0:4]
client_id, client_secret = Mastodon.create_app(app_name, api_base_url=instance)
self.db.cur.execute("INSERT INTO mastodon_instances(instance, client_id, client_secret) VALUES(?, ?, ?);",
(instance, client_id, client_secret))
self.db.conn.commit()
return client_id, client_secret
def save_masto_token(self, access_token, instance):
self.db.cur.execute("SELECT id FROM mastodon_instances WHERE instance = ?;", (instance, ))
instance_id = self.db.cur.fetchone()[0]
self.db.cur.execute("INSERT INTO mastodon_accounts(user_id, access_token, instance_id, active) "
"VALUES(?, ?, ?, ?);", (self.uid, access_token, instance_id, 1))
self.db.conn.commit()
def get_users(self):
self.cur.execute("SELECT id FROM user WHERE enabled=1;")
return self.cur.fetchall()
class DBPlugin(object):

View File

@ -5,6 +5,7 @@ import tweepy
import sendmail
import smtplib
from mastodon import Mastodon
import prepare
@get('/')
@ -25,12 +26,12 @@ def register_post(db):
return dict(error='Email address already in use.')
# send confirmation mail
confirm_link = request.url + "/../confirm/" + db.token(email, password)
db.send_confirmation_mail(confirm_link, email)
send_confirmation_mail(db.config, confirm_link, email)
return dict(info='Confirmation mail sent.')
def send_confirmation_mail(self, confirm_link, email):
m = sendmail.Mailer(self.config)
def send_confirmation_mail(config, confirm_link, email):
m = sendmail.Mailer(config)
try:
m.send("Complete your registration here: " + confirm_link, email, "[Ticketfrei] Confirm your account")
except smtplib.SMTPRecipientsRefused:
@ -142,5 +143,6 @@ def login_mastodon(user):
return dict(error='Login to Mastodon failed.')
config = prepare.get_config()
bottle.install(DBPlugin('/'))
bottle.run(host='localhost', port=8080)
bottle.run(host=config['web']['host'], port=8080)

19
prepare.py Normal file
View File

@ -0,0 +1,19 @@
import logging
import pytoml as toml
def get_logger(config):
logpath = config['logging']['logpath']
logger = logging.getLogger()
fh = logging.FileHandler(logpath)
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
return logger
def get_config():
# read config in TOML format (https://github.com/toml-lang/toml#toml)
with open('config.toml') as configfile:
config = toml.load(configfile)
return config

View File

@ -1,65 +1,31 @@
#!/usr/bin/env python3
import mastodon
import os
import pickle
import re
import time
import trigger
import sendmail
# import time
# import trigger
# import sendmail
import report
import backend
from user import User
class RetootBot(object):
def __init__(self, config):
def __init__(self, config, logger, uid, db):
self.config = config
self.logger = backend.get_logger(config)
self.client_id = self.register()
self.m = self.login()
self.logger = logger
self.user = User(db, uid)
client_id, client_secret, access_token, instance_url = self.user.get_masto_credentials()
self.m = mastodon.Mastodon(client_id=client_id, client_secret=client_secret,
access_token=access_token, api_base_url=instance_url)
# load state
try:
with open('seen_toots.pickle', 'rb') as f:
self.seen_toots = pickle.load(f)
except IOError:
self.seen_toots = set()
def register(self):
client_id = os.path.join(
'appkeys',
self.config['mapp']['name'] +
'@' + self.config['muser']['server']
)
if not os.path.isfile(client_id):
mastodon.Mastodon.create_app(
self.config['mapp']['name'],
api_base_url=self.config['muser']['server'],
to_file=client_id
)
return client_id
def login(self):
m = mastodon.Mastodon(
client_id=self.client_id,
api_base_url=self.config['muser']['server']
)
m.log_in(
self.config['muser']['email'],
self.config['muser']['password']
)
return m
self.seen_toots = self.user.get_seen_toot()
except TypeError:
self.seen_toots = 0
def save_last(self):
""" save the last seen toot """
try:
with os.fdopen(os.open('seen_toots.pickle.part', os.O_WRONLY | os.O_EXCL | os.O_CREAT), 'wb') as f:
pickle.dump(self.seen_toots, f)
except FileExistsError:
os.unlink('seen_toots.pickle.part')
with os.fdopen(os.open('seen_toots.pickle.part', os.O_WRONLY | os.O_EXCL | os.O_CREAT), 'wb') as f:
pickle.dump(self.seen_toots, f)
os.rename('seen_toots.pickle.part', 'seen_toots.pickle')
self.user.save_seen_toot(self.seen_toots)
def crawl(self):
"""
@ -74,9 +40,9 @@ class RetootBot(object):
self.logger.error("Unknown Mastodon API Error.", exc_info=True)
return mentions
for status in all:
if (status['type'] == 'mention' and status['status']['id'] not in self.seen_toots):
if status['type'] == 'mention' and status['status']['id'] > self.seen_toots:
# save state
self.seen_toots.add(status['status']['id'])
self.seen_toots = status['status']['id']
self.save_last()
# add mention to mentions
text = re.sub(r'<[^>]*>', '', status['status']['content'])
@ -124,7 +90,7 @@ class RetootBot(object):
# return mentions for mirroring
return retoots
"""
if __name__ == '__main__':
config = backend.get_config()
@ -146,3 +112,4 @@ if __name__ == '__main__':
attachment=config['logging']['logpath'])
except:
bot.logger.error('Mail sending failed', exc_info=True)
"""

View File

@ -22,14 +22,13 @@ class Mailer(object):
"""
# This generates the From address by stripping the part until the first
# period from the mail server address and won't work always.
self.fromaddr = config["mail"]["user"] + "@" + \
config["mail"]["mailserver"].partition(".")[2]
self.fromaddr = config["web"]["user"] + "@" + config["web"]["mailserver"].partition(".")[2]
# starts a client session with the SMTP server
self.s = smtplib.SMTP(config["mail"]["mailserver"])
self.s = smtplib.SMTP(config["web"]["mailserver"])
context = ssl.create_default_context()
self.s.starttls(context=context)
self.s.login(config["mail"]["user"], config["mail"]["passphrase"])
self.s.login(config["web"]["user"], config["web"]["passphrase"])
def send(self, text, recipient, subject, attachment=None):
"""
@ -66,8 +65,9 @@ class Mailer(object):
# For testing:
if __name__ == '__main__':
import backend
config = backend.get_config()
import prepare
config = prepare.get_config()
m = Mailer(config)
print(m.send("This is a test mail.", m.fromaddr, "Test"))

75
user.py Normal file
View File

@ -0,0 +1,75 @@
from bottle import response
from mastodon import Mastodon
class User(object):
def __init__(self, db, uid):
# set cookie
response.set_cookie('uid', uid, secret=db.secret, path='/')
self.db = db
self.uid = uid
def get_masto_credentials(self):
self.db.cur.execute("SELECT access_token, instance_id FROM mastodon_accounts WHERE user_id = ? AND active = 1;",
(self.uid, ))
row = self.db.cur.fetchone()
self.db.cur.execute("SELECT instance, client_id, client_secret FROM mastodon_instances WHERE id = ?;",
(row[1], ))
instance = self.db.cur.fetchone()
return instance[1], instance[2], row[0], instance[0]
def get_mastodon_account_id(self):
self.db.cur.execute("SELECT id FROM mastodon_accounts WHERE user_id = ?;", (self.uid, ))
return self.db.cur.fetchone()[0]
def get_seen_toot(self):
self.db.cur.execute("SELECT toot_id FROM seen_toots WHERE user_id = ? AND mastodon_accounts_id = ?;",
(self.uid, self.get_mastodon_account_id()))
return self.db.cur.fetchone()[0]
def save_seen_toot(self, toot_id):
self.db.cur.execute("UPDATE seen_toots SET toot_id = ? WHERE user_id = ? AND mastodon_accounts_id = ?;",
(toot_id, self.uid, self.get_mastodon_account_id()))
def state(self):
return dict(foo='bar')
def save_request_token(self, token):
self.db.cur.execute("INSERT INTO twitter_request_tokens(user_id, request_token) VALUES(?, ?);",
(self.uid, token))
self.db.conn.commit()
def get_request_token(self):
self.db.cur.execute("SELECT request_token FROM twitter_request_tokens WHERE user_id = ?;", (id,))
request_token = self.db.cur.fetchone()[0]
self.db.cur.execute("DELETE FROM twitter_request_tokens WHERE user_id = ?;", (id,))
self.db.conn.commit()
return request_token
def save_twitter_token(self, access_token, access_token_secret):
self.db.cur.execute(
"INSERT INTO twitter_accounts(user_id, access_token_key, access_token_secret) VALUES(?, ?, ?);",
(id, access_token, access_token_secret))
self.db.conn.commit()
def get_mastodon_app_keys(self, instance):
self.db.cur.execute("SELECT client_id, client_secret FROM mastodon_instances WHERE instance = ?;", (instance, ))
try:
row = self.db.cur.fetchone()
client_id = row[0]
client_secret = row[1]
return client_id, client_secret
except TypeError:
app_name = "ticketfrei" + str(self.db.secret)[0:4]
client_id, client_secret = Mastodon.create_app(app_name, api_base_url=instance)
self.db.cur.execute("INSERT INTO mastodon_instances(instance, client_id, client_secret) VALUES(?, ?, ?);",
(instance, client_id, client_secret))
self.db.conn.commit()
return client_id, client_secret
def save_masto_token(self, access_token, instance):
self.db.cur.execute("SELECT id FROM mastodon_instances WHERE instance = ?;", (instance, ))
instance_id = self.db.cur.fetchone()[0]
self.db.cur.execute("INSERT INTO mastodon_accounts(user_id, access_token, instance_id, active) "
"VALUES(?, ?, ?, ?);", (self.uid, access_token, instance_id, 1))
self.db.conn.commit()