curious-greg/web.py

153 lines
8.6 KiB
Python
Raw Normal View History

2018-11-01 14:25:47 +00:00
#!/usr/bin/env python3
#Curious Greg - Curious Cat to Mastodon crossposter
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
2018-11-11 11:31:33 +00:00
import requests, json, hashlib, urllib, time, re
2018-11-01 14:25:47 +00:00
from mastodon import Mastodon
2018-11-04 23:17:28 +00:00
from flask import Flask, render_template, request, session, redirect, url_for
2018-11-10 12:56:45 +00:00
import mysql.connector
2018-11-06 10:30:48 +00:00
import bcrypt
2018-11-01 14:25:47 +00:00
2018-11-02 02:35:40 +00:00
cfg = json.load(open("meta.json"))
2018-11-10 06:38:43 +00:00
scopes = ["read:accounts", "write:statuses"]
2018-11-02 02:35:40 +00:00
2018-11-10 12:56:45 +00:00
db = mysql.connector.connect(user=cfg['dbuser'], password=cfg['dbpass'], database=cfg['dbname'])
2018-11-01 14:25:47 +00:00
c = db.cursor()
# MariaDB [curiousgreg]> DESCRIBE data;
# +---------------------+--------------+------+-----+-------------------------------------------+-----------------------------+
# | Field | Type | Null | Key | Default | Extra |
# +---------------------+--------------+------+-----+-------------------------------------------+-----------------------------+
# | username | varchar(64) | NO | PRI | NULL | |
# | instance | varchar(128) | NO | PRI | NULL | |
# | password | tinytext | NO | | NULL | |
# | avi | text | NO | | NULL | |
# | secret | tinytext | NO | | NULL | |
# | client_id | varchar(128) | NO | | NULL | |
# | client_secret | tinytext | NO | | NULL | |
# | cc | tinytext | YES | | NULL | |
# | ccavi | varchar(128) | YES | | https://lynnesbian.space/res/ceres/cc.png | |
# | latest_post | tinytext | YES | | NULL | |
# | latest_timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
# | time_between_checks | int(11) | YES | | NULL | |
2018-11-11 11:31:33 +00:00
# | settings | longtext | YES | | NULL | |
# +---------------------+--------------+------+-----+-------------------------------------------+-----------------------------+
2018-11-11 11:31:33 +00:00
c.execute("CREATE TABLE IF NOT EXISTS `data` (username VARCHAR(64) NOT NULL, instance VARCHAR(128) NOT NULL, password TINYTEXT NOT NULL, avi TEXT NOT NULL, secret TINYTEXT NOT NULL, client_id VARCHAR(128) NOT NULL, client_secret TINYTEXT NOT NULL, cc TINYTEXT, ccavi VARCHAR(128) DEFAULT 'https://lynnesbian.space/res/ceres/cc.png', latest_post TINYTEXT, latest_timestamp TIMESTAMP, time_between_checks INT, settings LONGTEXT, PRIMARY KEY(username, instance))")
app = Flask(cfg['name'])
2018-11-02 03:13:08 +00:00
app.secret_key = cfg['flask_key']
2018-11-02 03:13:08 +00:00
@app.route('/')
def main():
2018-11-04 23:17:28 +00:00
if 'acct' not in session:
return render_template("landing_page.html")
else:
return redirect(url_for('home'))
@app.route('/home')
def home():
2018-11-06 09:11:03 +00:00
if 'acct' in session:
if 'cc' not in session:
session['cc'] = "None"
if session['cc'] == "None":
#every time home is rendered without cc being set
cc = c.execute("SELECT cc FROM `data` WHERE client_id LIKE ? AND instance LIKE ?", (session['client_id'], session['instance'])).fetchone()[0]
if cc != '':
session['cc'] = cc
if 'last_avi_update' not in session or session['last_avi_update'] + (24 * 60 * 60) < time.time():
#avatars haven't been updated for over 24 hours
# avis = c.execute("SELECT avi, ccavi FROM `data` WHERE client_id LIKE ?", (session['client_id'],)).fetchone()
client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance'])
session['avi'] = client.account_verify_credentials()['avatar']
if session['cc'] != None:
#update cc avi too
r = requests.get("https://curiouscat.me/api/v2/profile?username={}".format(session['cc']))
j = r.json()
session['ccavi'] = j['userData']['avatar']
c.execute("UPDATE data SET avi = ?, ccavi = ? WHERE client_id LIKE ? AND instance LIKE ?", (session['avi'], session['ccavi'], session['client_id'], session['instance']))
else:
c.execute("UPDATE data SET avi = ? WHERE client_id LIKE ? AND instance LIKE ?", (session['avi'], session['client_id'], session['instance']))
return render_template("home.html")
2018-11-06 09:11:03 +00:00
else:
return redirect(url_for('main'))
2018-11-06 11:22:10 +00:00
@app.route('/debug') #TODO: remove this before making the site live ;p
def print_debug_info():
return json.dumps(session._get_current_object())
@app.route('/login')
def log_in():
if 'acct' in session:
#user is probably already logged in. if they aren't, home() will handle things and redirect them back here
return redirect(url_for('home'))
return render_template("login.html")
# return(json.dumps(client_info))
#internal stuff
@app.route('/internal/auth_a')
def internal_auth_a(): #TODO: prevent these endpoints from being spammed somehow
2018-11-04 11:36:25 +00:00
session['instance'] = request.args.get('instance', default='mastodon.social', type=str)
if not session['instance'].startswith("https://"):
session['instance'] = "https://{}".format(session['instance'])
2018-11-06 09:11:03 +00:00
session['client_id'], session['client_secret'] = Mastodon.create_app(cfg['name'],
api_base_url=session['instance'],
2018-11-10 06:38:43 +00:00
scopes=scopes,
2018-11-06 09:11:03 +00:00
website=cfg['website'],
2018-11-10 06:38:43 +00:00
redirect_uris=['https://cg.lynnesbian.space/internal/auth_b', 'http://localhost:5000/internal/auth_b']
2018-11-04 23:17:28 +00:00
)
client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance'])
2018-11-10 06:38:43 +00:00
url = client.auth_request_url(client_id=session['client_id'], redirect_uris='http://localhost:5000/internal/auth_b', scopes=scopes)
return redirect(url, code=307)
2018-11-02 07:52:33 +00:00
2018-11-06 09:11:03 +00:00
@app.route('/internal/auth_b')
def internal_auth_b():
#write details to DB
client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance'])
2018-11-10 06:38:43 +00:00
session['secret'] = client.log_in(code = request.args.get('code'), scopes=scopes, redirect_uri='http://localhost:5000/internal/auth_b')
acct_info = client.account_verify_credentials()
session['username'] = acct_info['username']
session['avi'] = acct_info['avatar']
session['acct'] = "@{}@{}".format(session['username'], session['instance'].replace("https://", ""))
if c.execute("SELECT COUNT(*) FROM data WHERE username LIKE ? AND instance LIKE ?", (session['username'], session['instance'])).fetchone()[0] > 0:
2018-11-06 10:30:48 +00:00
#user already has an account with CG
#update the user's info to use the new info we just got, then redirect them to the login page
c.execute("UPDATE data SET client_id = ?, client_secret = ?, secret = ?, avi = ? WHERE username LIKE ? AND instance LIKE ?", (session['client_id'], session['client_secret'], session['secret'], session['avi'], session['username'], session['instance']))
2018-11-06 10:30:48 +00:00
return redirect(url_for('log_in'))
else:
return redirect(url_for('home'))
2018-11-06 09:11:03 +00:00
2018-11-06 11:22:10 +00:00
@app.route('/internal/do_login')
def do_login():
2018-11-11 11:31:33 +00:00
pw_in = request.form['pw']
pw_hashed = hashlib.sha256(pw_in.encode('utf-8'))
acct = request.form['acct']
session['username'] = re.match("^@[^@]*", acct).group(0)
session['instance'] = "https://{}".format(re.search("@([^@]+)$", acct).group(1))
pw = c.execute("SELECT password FROM data WHERE username LIKE ? AND password LIKE ?", (session['username'], session['instance'])).fetch_one()[0]
pw = bcrypt.hashpw(pw_hashed, bcrypt.gensalt(15))
2018-11-06 10:30:48 +00:00
2018-11-06 11:22:10 +00:00
@app.route('/create_password')
def create_password():
return render_template("create_password.html", bg = "\"background-image:url('{}')\"".format(session['avi']))
2018-11-06 23:10:50 +00:00
@app.route('/internal/create_account', methods=['POST'])
def create_account():
pw_in = request.form['pw']
if len(pw_in < 6) or pw_in == 'password':
return redirect('/create_password?invalid')
pw_hashed = hashlib.sha256(pw_in.encode('utf-8'))
pw = bcrypt.hashpw(pw_hashed, bcrypt.gensalt(15))
c.execute("INSERT INTO data (username, instance, avi, password, secret, client_id, client_secret) VALUES (?, ?, ?, ?, ?)", (session['username'], pw, session['instance'], session['secret'], session['client_id'], session['client_secret']))
db.commit()
return redirect(url_for('home'))