mirror of
https://github.com/Lynnesbian/FediBooks/
synced 2024-11-26 00:58:59 +00:00
Compare commits
No commits in common. "cbdddea33e083e6f3adf2840cb61207191007e4d" and "3a76d9f5ad5cff58b2035e4eb8237f3b0e77a7c7" have entirely different histories.
cbdddea33e
...
3a76d9f5ad
8 changed files with 18 additions and 139 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,4 +3,3 @@ config.json
|
||||||
planning.txt
|
planning.txt
|
||||||
*.pyc
|
*.pyc
|
||||||
/debug
|
/debug
|
||||||
lynnesbian.json
|
|
|
@ -1,11 +1,6 @@
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
import markovify
|
import markovify
|
||||||
import requests
|
|
||||||
from Crypto.PublicKey import RSA
|
|
||||||
from Crypto.Hash import SHA256
|
|
||||||
from Crypto.Signature import PKCS1_v1_5
|
|
||||||
from base64 import b64decode, b64encode
|
|
||||||
from mastodon import Mastodon, MastodonUnauthorizedError
|
from mastodon import Mastodon, MastodonUnauthorizedError
|
||||||
import html, re, json
|
import html, re, json
|
||||||
|
|
||||||
|
@ -180,67 +175,3 @@ def make_post(args):
|
||||||
c.execute("UPDATE bots SET last_post = CURRENT_TIMESTAMP() WHERE handle = %s", (handle,))
|
c.execute("UPDATE bots SET last_post = CURRENT_TIMESTAMP() WHERE handle = %s", (handle,))
|
||||||
db.commit()
|
db.commit()
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
def get_key():
|
|
||||||
db = MySQLdb.connect(
|
|
||||||
host = cfg['db_host'],
|
|
||||||
user=cfg['db_user'],
|
|
||||||
passwd=cfg['db_pass'],
|
|
||||||
db=cfg['db_name']
|
|
||||||
)
|
|
||||||
|
|
||||||
dc = db.cursor(MySQLdb.cursors.DictCursor)
|
|
||||||
dc.execute("SELECT * FROM http_auth_key")
|
|
||||||
key = dc.fetchone()
|
|
||||||
if key == None:
|
|
||||||
# generate new key
|
|
||||||
key = {}
|
|
||||||
privkey = RSA.generate(4096)
|
|
||||||
|
|
||||||
key['private'] = privkey.exportKey('PEM').decode('utf-8')
|
|
||||||
key['public'] = privkey.publickey().exportKey('PEM').decode('utf-8')
|
|
||||||
|
|
||||||
dc.execute("INSERT INTO http_auth_key (private, public) VALUES (%s, %s)", (key['private'], key['public']))
|
|
||||||
|
|
||||||
dc.close()
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
return key
|
|
||||||
|
|
||||||
def signed_get(url, timeout = 10, additional_headers = {}, request_json = True):
|
|
||||||
headers = {}
|
|
||||||
if request_json:
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
|
|
||||||
headers = {**headers, **additional_headers}
|
|
||||||
|
|
||||||
# sign request headers
|
|
||||||
key = RSA.importKey(get_key()['private'])
|
|
||||||
sigstring = ''
|
|
||||||
for header, value in headers.items():
|
|
||||||
sigstring += '{}: {}\n'.format(header.lower(), value)
|
|
||||||
|
|
||||||
sigstring.rstrip("\n")
|
|
||||||
|
|
||||||
pkcs = PKCS1_v1_5.new(key)
|
|
||||||
h = SHA256.new()
|
|
||||||
h.update(sigstring.encode('ascii'))
|
|
||||||
|
|
||||||
signed_sigstring = b64encode(pkcs.sign(h)).decode('ascii')
|
|
||||||
|
|
||||||
sig = {
|
|
||||||
'keyId': "{}/actor".format(cfg['base_uri']),
|
|
||||||
'algorithm': 'rsa-sha256',
|
|
||||||
'headers': ' '.join(headers.keys()),
|
|
||||||
'signature': signed_sigstring
|
|
||||||
}
|
|
||||||
|
|
||||||
sig_header = ['{}="{}"'.format(k, v) for k, v in sig.items()]
|
|
||||||
headers['signature'] = ','.join(sig_header)
|
|
||||||
|
|
||||||
r = requests.Request('GET', url, headers)
|
|
||||||
return r.headers
|
|
||||||
# return requests.get(url, timeout = timeout)
|
|
|
@ -5,16 +5,13 @@ import re, json
|
||||||
|
|
||||||
def bot_accounts_add(mysql, cfg):
|
def bot_accounts_add(mysql, cfg):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
# remove leading/trailing whitespace
|
|
||||||
if 'account' in request.form:
|
|
||||||
session['handle'] = request.form['account'].rstrip().lstrip()
|
|
||||||
if session['step'] == 1:
|
if session['step'] == 1:
|
||||||
if session['handle'] == session['bot']:
|
if request.form['account'] == session['bot']:
|
||||||
error = "Bots cannot learn from themselves."
|
error = "Bots cannot learn from themselves."
|
||||||
return render_template("bot/accounts_add.html", error = error)
|
return render_template("bot/accounts_add.html", error = error)
|
||||||
|
|
||||||
# look up user
|
# look up user
|
||||||
handle_list = session['handle'].split('@')
|
handle_list = request.form['account'].split('@')
|
||||||
if len(handle_list) != 3:
|
if len(handle_list) != 3:
|
||||||
# not formatted correctly
|
# not formatted correctly
|
||||||
error = "Incorrectly formatted handle."
|
error = "Incorrectly formatted handle."
|
||||||
|
@ -22,6 +19,7 @@ def bot_accounts_add(mysql, cfg):
|
||||||
|
|
||||||
session['username'] = handle_list[1]
|
session['username'] = handle_list[1]
|
||||||
session['instance'] = handle_list[2]
|
session['instance'] = handle_list[2]
|
||||||
|
session['handle'] = request.form['account']
|
||||||
|
|
||||||
if session['instance'] in json.load(open("blacklist.json")):
|
if session['instance'] in json.load(open("blacklist.json")):
|
||||||
session['error'] = "Learning from accounts on this instance is not allowed."
|
session['error'] = "Learning from accounts on this instance is not allowed."
|
||||||
|
@ -98,7 +96,7 @@ def bot_accounts_add(mysql, cfg):
|
||||||
return render_template("bot/accounts_add.html", error = error)
|
return render_template("bot/accounts_add.html", error = error)
|
||||||
|
|
||||||
# 2. use webfinger to find user's info page
|
# 2. use webfinger to find user's info page
|
||||||
# TODO: use more reliable method
|
#TODO: use more reliable method
|
||||||
try:
|
try:
|
||||||
uri = re.search(r'template="([^"]+)"', r.text).group(1)
|
uri = re.search(r'template="([^"]+)"', r.text).group(1)
|
||||||
uri = uri.format(uri = "{}@{}".format(session['username'], session['instance']))
|
uri = uri.format(uri = "{}@{}".format(session['username'], session['instance']))
|
||||||
|
@ -116,7 +114,7 @@ def bot_accounts_add(mysql, cfg):
|
||||||
found = False
|
found = False
|
||||||
for link in j['links']:
|
for link in j['links']:
|
||||||
if link['rel'] == 'self':
|
if link['rel'] == 'self':
|
||||||
# this is a link formatted like "https://instan.ce/users/username", which is what we need
|
#this is a link formatted like "https://instan.ce/users/username", which is what we need
|
||||||
uri = link['href']
|
uri = link['href']
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
|
@ -130,7 +128,7 @@ def bot_accounts_add(mysql, cfg):
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
# success!!
|
# success!!
|
||||||
c = mysql.connection.cursor()
|
c = mysql.connection.cursor()
|
||||||
c.execute("INSERT IGNORE INTO `fedi_accounts` (`handle`, `outbox`) VALUES (%s, %s)", (session['handle'], outbox))
|
c.execute("REPLACE INTO `fedi_accounts` (`handle`, `outbox`) VALUES (%s, %s)", (session['handle'], outbox))
|
||||||
c.execute("INSERT INTO `bot_learned_accounts` (`bot_id`, `fedi_id`) VALUES (%s, %s)", (session['bot'], session['handle']))
|
c.execute("INSERT INTO `bot_learned_accounts` (`bot_id`, `fedi_id`) VALUES (%s, %s)", (session['bot'], session['handle']))
|
||||||
c.close()
|
c.close()
|
||||||
mysql.connection.commit()
|
mysql.connection.commit()
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"@context": [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
{
|
|
||||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"endpoints": {},
|
|
||||||
"name": "FediBooks",
|
|
||||||
"type": "Application",
|
|
||||||
"id": "{{ base_uri }}/actor",
|
|
||||||
"manuallyApprovesFollowers": true,
|
|
||||||
"publicKey": {
|
|
||||||
"id": "{{ base_uri }}/actor#main-key",
|
|
||||||
"owner": "{{ base_uri }}/actor",
|
|
||||||
"publicKeyPem": "{{ pubkey }}"
|
|
||||||
},
|
|
||||||
"summary": "FediBooks Actor",
|
|
||||||
"preferredUsername": "fedibooks",
|
|
||||||
"url": "{{ base_uri }}/actor"
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"aliases": [
|
|
||||||
"{{ base_uri }}/actor"
|
|
||||||
],
|
|
||||||
"links": [
|
|
||||||
{
|
|
||||||
"href": "{{ base_uri }}/actor",
|
|
||||||
"rel": "self",
|
|
||||||
"type": "application/activity+json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"subject": "acct:fedibooks@{{ base_uri }}"
|
|
||||||
}
|
|
18
app/webui.py
18
app/webui.py
|
@ -32,11 +32,7 @@ scopes_pleroma = ['read', 'write', 'push']
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def login_check():
|
def login_check():
|
||||||
if request.path not in ['/', '/about', '/welcome', '/login', '/signup', '/do/login', '/do/signup'] \
|
if request.path not in ['/', '/about', '/welcome', '/login', '/signup', '/do/login', '/do/signup'] and not request.path.startswith("/push") and not request.path.startswith('/static'):
|
||||||
and not request.path.startswith("/push") \
|
|
||||||
and not request.path.startswith('/static') \
|
|
||||||
and not request.path.startswith('/actor') \
|
|
||||||
and not request.path.startswith('/.well-known'):
|
|
||||||
# page requires authentication
|
# page requires authentication
|
||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
return redirect(url_for('render_home'))
|
return redirect(url_for('render_home'))
|
||||||
|
@ -374,15 +370,9 @@ def img_bot_generic():
|
||||||
def favicon():
|
def favicon():
|
||||||
return send_file("static/favicon.ico")
|
return send_file("static/favicon.ico")
|
||||||
|
|
||||||
@app.route("/.well-known/webfinger")
|
# @app.route("/.well-known/webfinger")
|
||||||
def webfinger():
|
# def webfinger():
|
||||||
return render_template("ap/webfinger.json", base_uri = cfg['base_uri']), 200, {'Content-type':'application/json'}
|
# return render_template("webfinger.json", base_uri = cfg['base_uri']), 200, {'Content-type':'application/json'}
|
||||||
|
|
||||||
@app.route("/actor")
|
|
||||||
def actor():
|
|
||||||
# pubkey = functions.get_key()['public'].replace("\n", "\\n")
|
|
||||||
pubkey = functions.signed_get("https://fedi.lynnesbian.space/users/lynnesbian/outbox.json?page=true")
|
|
||||||
return render_template("ap/actor.json", base_uri = cfg['base_uri'], pubkey = pubkey), 200, {'Content-type':'application/json'}
|
|
||||||
|
|
||||||
|
|
||||||
def bot_check(bot):
|
def bot_check(bot):
|
||||||
|
|
20
db/setup.sql
20
db/setup.sql
|
@ -8,13 +8,13 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||||
`submit` ENUM('always', 'once', 'never') DEFAULT 'once',
|
`submit` ENUM('always', 'once', 'never') DEFAULT 'once',
|
||||||
`generation` ENUM('always', 'once', 'never') DEFAULT 'once',
|
`generation` ENUM('always', 'once', 'never') DEFAULT 'once',
|
||||||
`reply` ENUM('always', 'once', 'never') DEFAULT 'once'
|
`reply` ENUM('always', 'once', 'never') DEFAULT 'once'
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `credentials` (
|
CREATE TABLE IF NOT EXISTS `credentials` (
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
`client_id` VARCHAR(128) NOT NULL,
|
`client_id` VARCHAR(128) NOT NULL,
|
||||||
`client_secret` VARCHAR(128) NOT NULL,
|
`client_secret` VARCHAR(128) NOT NULL,
|
||||||
`secret` VARCHAR(128) NOT NULL
|
`secret` VARCHAR(128) NOT NULL
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `bots` (
|
CREATE TABLE IF NOT EXISTS `bots` (
|
||||||
`handle` VARCHAR(128) PRIMARY KEY,
|
`handle` VARCHAR(128) PRIMARY KEY,
|
||||||
`user_id` INT NOT NULL,
|
`user_id` INT NOT NULL,
|
||||||
|
@ -37,7 +37,7 @@ CREATE TABLE IF NOT EXISTS `bots` (
|
||||||
`icon_update_time` DATETIME DEFAULT '1000-01-01 00:00:00',
|
`icon_update_time` DATETIME DEFAULT '1000-01-01 00:00:00',
|
||||||
FOREIGN KEY (`user_id`) REFERENCES users(id) ON DELETE CASCADE,
|
FOREIGN KEY (`user_id`) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `fedi_accounts` (
|
CREATE TABLE IF NOT EXISTS `fedi_accounts` (
|
||||||
`handle` VARCHAR(128) PRIMARY KEY,
|
`handle` VARCHAR(128) PRIMARY KEY,
|
||||||
`outbox` VARCHAR(256),
|
`outbox` VARCHAR(256),
|
||||||
|
@ -45,14 +45,14 @@ CREATE TABLE IF NOT EXISTS `fedi_accounts` (
|
||||||
`icon` VARCHAR(512),
|
`icon` VARCHAR(512),
|
||||||
`icon_update_time` DATETIME DEFAULT 0,
|
`icon_update_time` DATETIME DEFAULT 0,
|
||||||
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
FOREIGN KEY (`credentials_id`) REFERENCES credentials(id) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `bot_learned_accounts` (
|
CREATE TABLE IF NOT EXISTS `bot_learned_accounts` (
|
||||||
`bot_id` VARCHAR(128) NOT NULL,
|
`bot_id` VARCHAR(128) NOT NULL,
|
||||||
`fedi_id` VARCHAR(128) NOT NULL,
|
`fedi_id` VARCHAR(128) NOT NULL,
|
||||||
`enabled` BOOLEAN DEFAULT 1,
|
`enabled` BOOLEAN DEFAULT 1,
|
||||||
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE,
|
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `posts` (
|
CREATE TABLE IF NOT EXISTS `posts` (
|
||||||
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
|
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
`fedi_id` VARCHAR(128),
|
`fedi_id` VARCHAR(128),
|
||||||
|
@ -60,14 +60,14 @@ CREATE TABLE IF NOT EXISTS `posts` (
|
||||||
`content` TEXT NOT NULL,
|
`content` TEXT NOT NULL,
|
||||||
`cw` BOOLEAN NOT NULL,
|
`cw` BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
FOREIGN KEY (`fedi_id`) REFERENCES fedi_accounts(handle) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `word_blacklist` (
|
CREATE TABLE IF NOT EXISTS `word_blacklist` (
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
`bot_id` VARCHAR(128) NOT NULL,
|
`bot_id` VARCHAR(128) NOT NULL,
|
||||||
`phrase` VARCHAR(128) NOT NULL,
|
`phrase` VARCHAR(128) NOT NULL,
|
||||||
`whole_word` BOOLEAN NOT NULL,
|
`whole_word` BOOLEAN NOT NULL,
|
||||||
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE
|
FOREIGN KEY (`bot_id`) REFERENCES bots(handle) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `contact_history` (
|
CREATE TABLE IF NOT EXISTS `contact_history` (
|
||||||
`user_id` INT NOT NULL,
|
`user_id` INT NOT NULL,
|
||||||
`fetch` BOOLEAN DEFAULT 0,
|
`fetch` BOOLEAN DEFAULT 0,
|
||||||
|
@ -75,8 +75,4 @@ CREATE TABLE IF NOT EXISTS `contact_history` (
|
||||||
`generation` BOOLEAN DEFAULT 0,
|
`generation` BOOLEAN DEFAULT 0,
|
||||||
`reply` BOOLEAN DEFAULT 0,
|
`reply` BOOLEAN DEFAULT 0,
|
||||||
FOREIGN KEY (`user_id`) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY (`user_id`) REFERENCES users(id) ON DELETE CASCADE
|
||||||
) ENGINE = INNODB;
|
) ENGINE=INNODB;
|
||||||
CREATE TABLE IF NOT EXISTS `http_auth_key` (
|
|
||||||
`private` TEXT NOT NULL,
|
|
||||||
`public` TEXT NOT NULL
|
|
||||||
) ENGINE = INNODB;
|
|
||||||
|
|
|
@ -7,5 +7,4 @@ flask-mysqldb==0.2.0
|
||||||
bcrypt == 3.1.7
|
bcrypt == 3.1.7
|
||||||
requests==2.23.0
|
requests==2.23.0
|
||||||
http-ece==1.1.0
|
http-ece==1.1.0
|
||||||
pycryptodome==3.9.7
|
|
||||||
cryptography==2.9
|
cryptography==2.9
|
||||||
|
|
Loading…
Reference in a new issue