2018-03-28 22:12:19 +00:00
|
|
|
#!/usr/bin/env python3
|
2018-03-22 01:23:31 +00:00
|
|
|
import bottle
|
|
|
|
from bottle import get, post, redirect, request, response, view
|
2018-03-24 15:26:35 +00:00
|
|
|
from config import config
|
2018-03-28 15:36:35 +00:00
|
|
|
from db import db
|
2018-03-24 15:26:35 +00:00
|
|
|
import logging
|
2018-03-23 01:28:00 +00:00
|
|
|
import tweepy
|
2018-03-28 22:57:17 +00:00
|
|
|
from sendmail import sendmail
|
2018-03-28 15:36:35 +00:00
|
|
|
from session import SessionPlugin
|
2018-03-23 01:28:00 +00:00
|
|
|
from mastodon import Mastodon
|
2018-03-24 15:26:35 +00:00
|
|
|
|
2018-09-08 09:14:00 +00:00
|
|
|
|
2018-03-29 00:40:22 +00:00
|
|
|
def url(route):
|
2018-03-29 19:58:55 +00:00
|
|
|
return '%s://%s/%s' % (
|
|
|
|
request.urlparts.scheme,
|
|
|
|
request.urlparts.netloc,
|
|
|
|
route)
|
2018-03-29 00:40:22 +00:00
|
|
|
|
|
|
|
|
2018-03-22 01:23:31 +00:00
|
|
|
@get('/')
|
|
|
|
@view('template/propaganda.tpl')
|
|
|
|
def propaganda():
|
2018-03-22 10:22:28 +00:00
|
|
|
pass
|
2018-03-22 01:23:31 +00:00
|
|
|
|
2018-03-23 01:28:00 +00:00
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@post('/register')
|
2018-03-22 01:23:31 +00:00
|
|
|
@view('template/register.tpl')
|
2018-03-28 15:36:35 +00:00
|
|
|
def register_post():
|
2018-04-14 15:53:08 +00:00
|
|
|
try:
|
|
|
|
email = request.forms['email']
|
|
|
|
password = request.forms['pass']
|
|
|
|
password_repeat = request.forms['pass-repeat']
|
2018-05-25 12:44:45 +00:00
|
|
|
city = request.forms['city']
|
2018-04-14 15:53:08 +00:00
|
|
|
except KeyError:
|
|
|
|
return dict(error='Please, fill the form.')
|
2018-03-22 01:23:31 +00:00
|
|
|
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
|
2018-03-28 23:13:53 +00:00
|
|
|
try:
|
2018-09-09 12:57:40 +00:00
|
|
|
link = url('confirm/' + city + '/%s' % db.user_token(email, password))
|
|
|
|
print(link) # only for local testing
|
2018-09-09 13:09:40 +00:00
|
|
|
logger.error('confirmation link to ' + email + ": " + link)
|
2018-03-28 23:13:53 +00:00
|
|
|
sendmail(
|
|
|
|
email,
|
2018-03-29 00:40:22 +00:00
|
|
|
"Confirm your account",
|
2018-10-07 19:02:48 +00:00
|
|
|
body="Complete your registration here: %s" % (link)
|
2018-03-28 23:13:53 +00:00
|
|
|
)
|
|
|
|
return dict(info='Confirmation mail sent.')
|
|
|
|
except Exception:
|
2018-04-15 10:11:49 +00:00
|
|
|
logger.error("Could not send confirmation mail to " + email, exc_info=True)
|
2018-03-28 23:13:53 +00:00
|
|
|
return dict(error='Could not send confirmation mail.')
|
2018-03-22 01:23:31 +00:00
|
|
|
|
|
|
|
|
2018-05-25 12:44:45 +00:00
|
|
|
@get('/confirm/<city>/<token>')
|
2018-03-22 01:23:31 +00:00
|
|
|
@view('template/propaganda.tpl')
|
2018-05-25 12:44:45 +00:00
|
|
|
def confirm(city, token):
|
2019-01-11 11:15:28 +00:00
|
|
|
# check whether city already exists
|
|
|
|
if db.by_city(city):
|
2019-01-11 12:21:47 +00:00
|
|
|
return dict(error='This Account was already confirmed, please try '
|
|
|
|
'signing in.')
|
2018-03-22 01:23:31 +00:00
|
|
|
# create db-entry
|
2018-05-25 12:44:45 +00:00
|
|
|
if db.confirm(token, city):
|
2018-03-22 10:22:28 +00:00
|
|
|
# :todo show info "Account creation successful."
|
2018-04-14 15:34:43 +00:00
|
|
|
redirect('/settings')
|
2019-01-11 12:21:47 +00:00
|
|
|
return dict(error='Account creation failed. Please try to register again.')
|
2018-03-22 01:23:31 +00:00
|
|
|
|
|
|
|
|
2019-01-11 12:38:47 +00:00
|
|
|
@get('/version')
|
|
|
|
def version():
|
|
|
|
import git
|
|
|
|
repo = git.Repo(search_parent_directories=True)
|
|
|
|
return repo.head.object.hexsha
|
|
|
|
|
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@post('/login')
|
2018-03-22 01:23:31 +00:00
|
|
|
@view('template/login.tpl')
|
2018-03-28 15:36:35 +00:00
|
|
|
def login_post():
|
2018-03-22 01:23:31 +00:00
|
|
|
# check login
|
2018-03-28 23:13:53 +00:00
|
|
|
try:
|
2018-04-14 15:53:08 +00:00
|
|
|
if db.by_email(request.forms['email']) \
|
|
|
|
.check_password(request.forms['pass']):
|
2018-04-14 15:34:43 +00:00
|
|
|
redirect('/settings')
|
2018-04-14 15:53:08 +00:00
|
|
|
except KeyError:
|
|
|
|
return dict(error='Please, fill the form.')
|
2018-03-28 23:13:53 +00:00
|
|
|
except AttributeError:
|
|
|
|
pass
|
2018-03-29 19:58:55 +00:00
|
|
|
return dict(error='Authentication failed.')
|
2018-03-22 01:23:31 +00:00
|
|
|
|
|
|
|
|
2018-04-26 19:50:52 +00:00
|
|
|
@get('/city/<city>')
|
2018-09-08 09:14:00 +00:00
|
|
|
def city_page(city, info=None):
|
2018-05-25 12:44:45 +00:00
|
|
|
citydict = db.user_facing_properties(city)
|
|
|
|
if citydict is not None:
|
2018-09-08 09:14:00 +00:00
|
|
|
citydict['info'] = info
|
|
|
|
return bottle.template('template/city.tpl', **citydict)
|
|
|
|
return bottle.template('template/propaganda.tpl',
|
|
|
|
**dict(info='There is no Ticketfrei bot in your city'
|
|
|
|
' yet. Create one yourself!'))
|
2018-04-26 19:50:52 +00:00
|
|
|
|
|
|
|
|
2018-07-22 11:47:56 +00:00
|
|
|
@get('/city/mail/<city>')
|
|
|
|
@view('template/mail.tpl')
|
2018-08-07 13:10:28 +00:00
|
|
|
def display_mail_page(city):
|
|
|
|
user = db.by_city(city)
|
2018-07-22 11:47:56 +00:00
|
|
|
return user.state()
|
|
|
|
|
|
|
|
|
|
|
|
@post('/city/mail/submit/<city>')
|
2018-08-07 13:10:28 +00:00
|
|
|
def subscribe_mail(city):
|
2018-07-22 11:47:56 +00:00
|
|
|
email = request.forms['mailaddress']
|
2018-08-08 08:24:20 +00:00
|
|
|
token = db.mail_subscription_token(email, city)
|
|
|
|
confirm_link = url('city/mail/confirm/' + token)
|
|
|
|
print(confirm_link) # only for local testing
|
2018-08-07 13:10:28 +00:00
|
|
|
# send mail with code to email
|
2018-08-08 08:24:20 +00:00
|
|
|
sendmail(email, "Subscribe to Ticketfrei " + city + " Mail Notifications",
|
2018-09-08 09:14:00 +00:00
|
|
|
body="To subscribe to the mail notifications for Ticketfrei " +
|
2018-10-07 19:02:48 +00:00
|
|
|
city + ", click on this link: " + confirm_link, city=city)
|
2018-09-08 09:14:00 +00:00
|
|
|
return city_page(city, info="Thanks! You will receive a confirmation mail.")
|
2018-08-07 13:10:28 +00:00
|
|
|
|
|
|
|
|
2018-08-08 08:24:20 +00:00
|
|
|
@get('/city/mail/confirm/<token>')
|
|
|
|
def confirm_subscribe(token):
|
|
|
|
email, city = db.confirm_subscription(token)
|
2018-08-07 13:10:28 +00:00
|
|
|
user = db.by_city(city)
|
2018-07-22 11:56:15 +00:00
|
|
|
user.add_subscriber(email)
|
2018-09-08 09:14:00 +00:00
|
|
|
return city_page(city, info="Thanks for subscribing to mail notifications!")
|
2018-07-22 11:47:56 +00:00
|
|
|
|
|
|
|
|
2018-08-09 13:01:51 +00:00
|
|
|
@get('/city/mail/unsubscribe/<token>')
|
|
|
|
def unsubscribe(token):
|
|
|
|
email, city = db.confirm_subscription(token)
|
|
|
|
user = db.by_city(city)
|
|
|
|
user.remove_subscriber(email)
|
2018-09-08 09:14:00 +00:00
|
|
|
return city_page(city, info="You successfully unsubscribed " + email +
|
2018-09-13 15:33:33 +00:00
|
|
|
" from the mail notifications.")
|
2018-08-09 13:01:51 +00:00
|
|
|
|
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@get('/settings')
|
2018-03-22 01:23:31 +00:00
|
|
|
@view('template/settings.tpl')
|
|
|
|
def settings(user):
|
|
|
|
return user.state()
|
|
|
|
|
|
|
|
|
2018-05-25 14:50:02 +00:00
|
|
|
@post('/settings/markdown')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-05-25 14:50:02 +00:00
|
|
|
@view('template/settings.tpl')
|
|
|
|
def update_markdown(user):
|
|
|
|
user.set_markdown(request.forms['markdown'])
|
|
|
|
return user.state()
|
|
|
|
|
|
|
|
|
2018-09-08 09:14:00 +00:00
|
|
|
@post('/settings/mail_md')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-09-08 09:14:00 +00:00
|
|
|
@view('template/settings.tpl')
|
|
|
|
def update_mail_md(user):
|
|
|
|
user.set_mail_md(request.forms['mail_md'])
|
|
|
|
return user.state()
|
|
|
|
|
2018-09-13 15:33:33 +00:00
|
|
|
|
2018-05-25 13:57:20 +00:00
|
|
|
@post('/settings/goodlist')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-05-25 13:57:20 +00:00
|
|
|
@view('template/settings.tpl')
|
|
|
|
def update_trigger_patterns(user):
|
|
|
|
user.set_trigger_words(request.forms['goodlist'])
|
|
|
|
return user.state()
|
|
|
|
|
|
|
|
|
2018-09-13 17:54:51 +00:00
|
|
|
@post('/settings/blocklist')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-05-25 13:57:20 +00:00
|
|
|
@view('template/settings.tpl')
|
|
|
|
def update_badwords(user):
|
2018-09-13 17:54:51 +00:00
|
|
|
user.set_badwords(request.forms['blocklist'])
|
2018-05-25 13:57:20 +00:00
|
|
|
return user.state()
|
|
|
|
|
|
|
|
|
2018-06-23 22:00:48 +00:00
|
|
|
@post('/settings/telegram')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-06-23 22:00:48 +00:00
|
|
|
def register_telegram(user):
|
|
|
|
apikey = request.forms['apikey']
|
2018-09-09 14:58:07 +00:00
|
|
|
user.update_telegram_key(apikey)
|
|
|
|
return city_page(user.get_city(), info="Thanks for registering Telegram!")
|
2018-06-23 22:00:48 +00:00
|
|
|
|
|
|
|
|
2019-01-27 13:52:42 +00:00
|
|
|
# unused afaik
|
|
|
|
#@get('/api/state')
|
|
|
|
#def api_enable(user):
|
|
|
|
# return user.state()
|
2018-03-22 01:23:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
@get('/static/<filename:path>')
|
|
|
|
def static(filename):
|
|
|
|
return bottle.static_file(filename, root='static')
|
|
|
|
|
2018-03-23 01:28:00 +00:00
|
|
|
|
2018-04-26 19:50:52 +00:00
|
|
|
@get('/guides/<filename:path>')
|
|
|
|
def guides(filename):
|
|
|
|
return bottle.static_file(filename, root='guides')
|
|
|
|
|
|
|
|
|
2018-03-22 10:22:28 +00:00
|
|
|
@get('/logout/')
|
|
|
|
def logout():
|
|
|
|
# clear auth cookie
|
|
|
|
response.set_cookie('uid', '', expires=0, path="/")
|
2019-01-27 13:52:42 +00:00
|
|
|
response.set_cookie('csrf', '', expires=0, path="/")
|
2018-03-22 10:22:28 +00:00
|
|
|
# :todo show info "Logout successful."
|
2019-01-27 13:52:42 +00:00
|
|
|
response.set_cookie('csrf', '', expires=0, path="/")
|
2018-04-14 15:34:43 +00:00
|
|
|
redirect('/')
|
2018-03-22 10:22:28 +00:00
|
|
|
|
2018-03-22 01:23:31 +00:00
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@get('/login/twitter')
|
2018-03-23 01:28:00 +00:00
|
|
|
def login_twitter(user):
|
|
|
|
"""
|
|
|
|
Starts the twitter OAuth authentication process.
|
|
|
|
:return: redirect to twitter.
|
|
|
|
"""
|
2018-04-14 15:31:01 +00:00
|
|
|
consumer_key = config["twitter"]["consumer_key"]
|
|
|
|
consumer_secret = config["twitter"]["consumer_secret"]
|
|
|
|
callback_url = url("login/twitter/callback")
|
2018-03-23 01:28:00 +00:00
|
|
|
auth = tweepy.OAuthHandler(consumer_key, consumer_secret, callback_url)
|
|
|
|
try:
|
|
|
|
redirect_url = auth.get_authorization_url()
|
|
|
|
except tweepy.TweepError:
|
2018-03-24 15:26:35 +00:00
|
|
|
logger.error('Twitter OAuth Error: Failed to get request token.',
|
|
|
|
exc_info=True)
|
2018-03-23 12:14:06 +00:00
|
|
|
return dict(error="Failed to get request token.")
|
2018-03-23 01:28:00 +00:00
|
|
|
user.save_request_token(auth.request_token)
|
2018-04-14 15:34:43 +00:00
|
|
|
redirect(redirect_url)
|
2018-03-23 01:28:00 +00:00
|
|
|
|
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@get('/login/twitter/callback')
|
2018-03-23 01:28:00 +00:00
|
|
|
def twitter_callback(user):
|
|
|
|
"""
|
|
|
|
Gets the callback
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
# twitter passes the verifier/oauth token secret in a GET request.
|
2018-04-14 15:38:49 +00:00
|
|
|
verifier = request.query['oauth_verifier']
|
2018-03-25 15:50:28 +00:00
|
|
|
consumer_key = config["twitter"]["consumer_key"]
|
|
|
|
consumer_secret = config["twitter"]["consumer_secret"]
|
2018-03-23 01:28:00 +00:00
|
|
|
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
|
2018-04-14 15:49:19 +00:00
|
|
|
request_token = user.get_request_token()
|
2018-04-14 16:16:05 +00:00
|
|
|
auth.request_token = request_token
|
2018-03-23 01:28:00 +00:00
|
|
|
auth.get_access_token(verifier)
|
|
|
|
user.save_twitter_token(auth.access_token, auth.access_token_secret)
|
2018-04-14 15:34:43 +00:00
|
|
|
redirect("/settings")
|
2018-03-23 01:28:00 +00:00
|
|
|
|
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
@post('/login/mastodon')
|
2019-01-27 13:52:42 +00:00
|
|
|
#csrf
|
2018-03-23 01:28:00 +00:00
|
|
|
def login_mastodon(user):
|
|
|
|
"""
|
2018-10-05 22:56:12 +00:00
|
|
|
Mastodon OAuth authentication process.
|
|
|
|
:return: redirect to city page.
|
2018-03-23 01:28:00 +00:00
|
|
|
"""
|
|
|
|
# get app tokens
|
2018-03-24 15:26:35 +00:00
|
|
|
instance_url = request.forms.get('instance_url')
|
|
|
|
masto_email = request.forms.get('email')
|
|
|
|
masto_pass = request.forms.get('pass')
|
2018-03-23 01:28:00 +00:00
|
|
|
client_id, client_secret = user.get_mastodon_app_keys(instance_url)
|
2018-03-24 15:26:35 +00:00
|
|
|
m = Mastodon(client_id=client_id, client_secret=client_secret,
|
|
|
|
api_base_url=instance_url)
|
2018-03-23 01:28:00 +00:00
|
|
|
try:
|
|
|
|
access_token = m.log_in(masto_email, masto_pass)
|
|
|
|
user.save_masto_token(access_token, instance_url)
|
2018-09-09 18:22:41 +00:00
|
|
|
return city_page(user.get_city(), info='Thanks for supporting decentralized social networks!')
|
2018-03-28 23:25:17 +00:00
|
|
|
except Exception:
|
2018-03-24 15:26:35 +00:00
|
|
|
logger.error('Login to Mastodon failed.', exc_info=True)
|
2018-03-23 01:28:00 +00:00
|
|
|
return dict(error='Login to Mastodon failed.')
|
|
|
|
|
|
|
|
|
2018-03-28 21:33:04 +00:00
|
|
|
logger = logging.getLogger()
|
2018-04-14 13:22:05 +00:00
|
|
|
fh = logging.FileHandler('/var/log/ticketfrei/error.log')
|
2018-03-28 21:33:04 +00:00
|
|
|
fh.setLevel(logging.DEBUG)
|
|
|
|
logger.addHandler(fh)
|
|
|
|
|
2018-03-28 15:36:35 +00:00
|
|
|
application = bottle.default_app()
|
|
|
|
bottle.install(SessionPlugin('/'))
|
|
|
|
|
2018-03-24 14:02:11 +00:00
|
|
|
if __name__ == '__main__':
|
2018-11-07 00:57:47 +00:00
|
|
|
bottle.run(host="0.0.0.0", port=config["web"]["port"])
|
2018-04-14 15:31:01 +00:00
|
|
|
else:
|
|
|
|
application.catchall = False
|