ticketfrei/ticketfrei/frontend.py

281 lines
8.3 KiB
Python
Executable File

#!/usr/bin/env python3
import bottle
from os import listdir, path
from bottle import get, post, redirect, request, response, view
from config import config, STATIC_DIR, TEMPLATE_DIR
from db import db
import logging
import tweepy
from sendmail import sendmail
from session import SessionPlugin
from mastodon import Mastodon
def url(route):
return '%s://%s/%s' % (
request.urlparts.scheme,
request.urlparts.netloc,
route)
@get('/')
@view('propaganda.tpl')
def propaganda():
pass
@post('/register')
@view('register.tpl')
def register_post():
try:
email = request.forms['email']
password = request.forms['pass']
password_repeat = request.forms['pass-repeat']
city = request.forms['city']
except KeyError:
return dict(error='Please, fill the form.')
if password != password_repeat:
return dict(error='Passwords do not match.')
if db.by_email(email):
return dict(error='Email address already in use.')
# send confirmation mail
try:
link = url('confirm/' + city + '/%s' % db.user_token(email, password))
print(link) # only for local testing
logger.error('confirmation link to ' + email + ": " + link)
sendmail(
email,
"Confirm your account",
body="Complete your registration here: %s" % (link)
)
return dict(info='Confirmation mail sent.')
except Exception:
logger.error("Could not send confirmation mail to " + email, exc_info=True)
return dict(error='Could not send confirmation mail.')
@get('/confirm/<city>/<token>')
@view('propaganda.tpl')
def confirm(city, token):
# check whether city already exists
if db.by_city(city):
return dict(error='This Account was already confirmed, please try '
'signing in.')
# create db-entry
if db.confirm(token, city):
# :todo show info "Account creation successful."
redirect('/settings')
return dict(error='Account creation failed. Please try to register again.')
@get('/version')
def version():
import git
repo = git.Repo(search_parent_directories=True)
return repo.head.object.hexsha
@post('/login')
@view('login.tpl')
def login_post():
# check login
try:
if db.by_email(request.forms['email']) \
.check_password(request.forms['pass']):
redirect('/settings')
except KeyError:
return dict(error='Please, fill the form.')
except AttributeError:
pass
return dict(error='Authentication failed.')
@get('/city/<city>')
def city_page(city, info=None):
citydict = db.user_facing_properties(city)
if citydict is not None:
citydict['info'] = info
return bottle.template('city.tpl', **citydict)
return bottle.template('propaganda.tpl',
**dict(info='There is no Ticketfrei bot in your city'
' yet. Create one yourself!'))
@get('/city/mail/<city>')
@view('mail.tpl')
def display_mail_page(city):
user = db.by_city(city)
return user.state()
@post('/city/mail/submit/<city>')
def subscribe_mail(city):
email = request.forms['mailaddress']
token = db.mail_subscription_token(email, city)
confirm_link = url('city/mail/confirm/' + token)
print(confirm_link) # only for local testing
# send mail with code to email
sendmail(email, "Subscribe to Ticketfrei " + city + " Mail Notifications",
body="To subscribe to the mail notifications for Ticketfrei " +
city + ", click on this link: " + confirm_link, city=city)
return city_page(city, info="Thanks! You will receive a confirmation mail.")
@get('/city/mail/confirm/<token>')
def confirm_subscribe(token):
email, city = db.confirm_subscription(token)
user = db.by_city(city)
user.add_subscriber(email)
return city_page(city, info="Thanks for subscribing to mail notifications!")
@get('/city/mail/unsubscribe/<token>')
def unsubscribe(token):
email, city = db.confirm_subscription(token)
user = db.by_city(city)
user.remove_subscriber(email)
return city_page(city, info="You successfully unsubscribed " + email +
" from the mail notifications.")
@get('/settings')
@view('settings.tpl')
def settings(user):
return user.state()
@post('/settings/markdown')
@view('settings.tpl')
def update_markdown(user):
user.set_markdown(request.forms['markdown'])
return user.state()
@post('/settings/mail_md')
@view('settings.tpl')
def update_mail_md(user):
user.set_mail_md(request.forms['mail_md'])
return user.state()
@post('/settings/goodlist')
@view('settings.tpl')
def update_trigger_patterns(user):
user.set_trigger_words(request.forms['goodlist'])
return user.state()
@post('/settings/blocklist')
@view('settings.tpl')
def update_badwords(user):
user.set_badwords(request.forms['blocklist'])
return user.state()
@post('/settings/telegram')
def register_telegram(user):
apikey = request.forms['apikey']
user.update_telegram_key(apikey)
return city_page(user.get_city(), info="Thanks for registering Telegram!")
# unused afaik
#@get('/api/state')
#def api_enable(user):
# return user.state()
@get('/static/<filename:path>')
def static(filename):
return bottle.static_file(filename, root=STATIC_DIR)
# IS THIS USED?
#@get('/guides/<filename:path>')
#def guides(filename):
# return bottle.static_file(filename, root='guides')
@get('/logout/')
def logout():
# clear auth cookie
response.set_cookie('uid', '', expires=0, path="/")
response.set_cookie('csrf', '', expires=0, path="/")
# :todo show info "Logout successful."
redirect('/')
@get('/login/twitter')
def login_twitter(user):
"""
Starts the twitter OAuth authentication process.
:return: redirect to twitter.
"""
consumer_key = config["twitter"]["consumer_key"]
consumer_secret = config["twitter"]["consumer_secret"]
callback_url = url("login/twitter/callback")
auth = tweepy.OAuthHandler(consumer_key, consumer_secret, callback_url)
try:
redirect_url = auth.get_authorization_url()
except tweepy.TweepError:
logger.error('Twitter OAuth Error: Failed to get request token.',
exc_info=True)
return dict(error="Failed to get request token.")
user.save_request_token(auth.request_token)
redirect(redirect_url)
@get('/login/twitter/callback')
def twitter_callback(user):
"""
Gets the callback
:return:
"""
# twitter passes the verifier/oauth token secret in a GET request.
verifier = request.query['oauth_verifier']
consumer_key = config["twitter"]["consumer_key"]
consumer_secret = config["twitter"]["consumer_secret"]
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
request_token = user.get_request_token()
auth.request_token = request_token
auth.get_access_token(verifier)
user.save_twitter_token(auth.access_token, auth.access_token_secret)
redirect("/settings")
@post('/login/mastodon')
def login_mastodon(user):
"""
Mastodon OAuth authentication process.
:return: redirect to city page.
"""
# get app tokens
instance_url = request.forms.get('instance_url')
masto_email = request.forms.get('email')
masto_pass = request.forms.get('pass')
client_id, client_secret = user.get_mastodon_app_keys(instance_url)
m = Mastodon(client_id=client_id, client_secret=client_secret,
api_base_url=instance_url)
try:
access_token = m.log_in(masto_email, masto_pass)
user.save_masto_token(access_token, instance_url)
return city_page(user.get_city(), info='Thanks for supporting decentralized social networks!')
except Exception:
logger.error('Login to Mastodon failed.', exc_info=True)
return dict(error='Login to Mastodon failed.')
logger = logging.getLogger()
fh = logging.FileHandler(config['log']['log_frontend'])
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
# TODO change TEMPLATE_PATH to BOTS_DIR after refactoring
bottle.TEMPLATE_PATH.insert(0, TEMPLATE_DIR)
application = bottle.default_app()
bottle.install(SessionPlugin('/'))
if __name__ == '__main__':
bottle.run(host="0.0.0.0", port=config["web"]["port"])
else:
application.catchall = False