from flask import session, render_template, request, redirect import requests from mastodon import Mastodon import re def bot_accounts_add(mysql, cfg): if request.method == 'POST': if session['step'] == 1: if request.form['account'] == session['bot']: error = "Bots cannot learn from themselves." return render_template("bot/accounts_add.html", error = error) # look up user handle_list = request.form['account'].split('@') if len(handle_list) != 3: # not formatted correctly error = "Incorrectly formatted handle." return render_template("bot/accounts_add.html", error = error) username = handle_list[1] instance = handle_list[2] # gab check try: r = requests.get("https://{}/api/v1/instance".format(instance), timeout=10) except requests.exceptions.ConnectionError: error = "Couldn't connect to {}.".format(instance) return render_template("bot/accounts_add.html", error = error) except: error = "An unknown error occurred." return render_template("bot/accounts_add.html", error = error) if r.status_code == 200: j = r.json() if 'contact_account' in j and 'is_pro' in j['contact_account']: # gab instance error = "Gab instances are not supported." return render_template("bot/accounts_add.html", error = error) # 1. download host-meta to find webfinger URL r = requests.get("https://{}/.well-known/host-meta".format(instance), timeout=10) if r.status_code != 200: error = "Couldn't get host-meta." return render_template("bot/accounts_add.html", error = error) # 2. use webfinger to find user's info page #TODO: use more reliable method try: uri = re.search(r'template="([^"]+)"', r.text).group(1) uri = uri.format(uri = "{}@{}".format(username, instance)) except: error = "Couldn't find WebFinger URL." return render_template("bot/accounts_add.html", error = error) r = requests.get(uri, headers={"Accept": "application/json"}, timeout=10) try: j = r.json() except: error = "Invalid WebFinger response." return render_template("bot/accounts_add.html", error = error) found = False for link in j['links']: if link['rel'] == 'self': #this is a link formatted like "https://instan.ce/users/username", which is what we need uri = link['href'] found = True break if not found: error = "Couldn't find a valid ActivityPub outbox URL." return render_template("bot/accounts_add.html", error = error) # 3. format as outbox URL and check to make sure it works outbox = "{}/outbox?page=true".format(uri) r = requests.get(uri, headers={"Accept": "application/json"}, timeout=10) if r.status_code == 200: # success!! c = mysql.connection.cursor() c.execute("REPLACE INTO `fedi_accounts` (`handle`, `outbox`) VALUES (%s, %s)", (request.form['account'], outbox)) c.execute("INSERT INTO `bot_learned_accounts` (`bot_id`, `fedi_id`) VALUES (%s, %s)", (session['bot'], request.form['account'])) c.close() mysql.connection.commit() if 'account' in cfg: client = Mastodon( cfg['account']['client_id'], cfg['account']['client_secret'], cfg['account']['secret'], "https://{}".format(cfg['account']['instance']) ) status = """ Hi, {user}. Someone has created a FediBooks bot that learns from your posts. The bot's username is {bot}. Your public posts are now being downloaded and stored for use by this bot. If you do not want {bot} to learn from your posts, click here: {overview} If you want to ensure that nobody can use {home} to create bots that use your post history, click here: {blacklist} """.format( user = request.form['account'], bot = session['bot'].replace("@", "@\u200B"), overview = "{}/overview/{}".format(cfg['base_uri'], request.form['account']), blacklist = "{}/blacklist".format(cfg['base_uri']), home = cfg['base_uri'] ) client.status_post(status) return redirect("/bot/accounts/{}".format(session['bot']), 303) else: error = "Couldn't access ActivityPub outbox. {} may require authenticated fetches, which FediBooks doesn't support yet.".format(instance) return render_template("bot/accounts_add.html", error = error) else: # new account add request session['step'] = 1 return render_template("bot/accounts_add.html", error = session.pop('error', None))