def login(config: CleanTootsConfig, only_missing: bool): """Fetch credentials for each app described in config file.""" if not _config_has_sections(config): return prompt = True for section in config.sections(): section = config[section] app_file_exists = config.isfile(section.get("app_secret_file")) user_file_exists = config.isfile(section.get("user_secret_file")) if not (only_missing and app_file_exists): Mastodon.create_app( "cleantoots", api_base_url=section.get("api_base_url"), to_file=config.file(section.get("app_secret_file")), ) mastodon = Mastodon( client_id=config.file(section.get("app_secret_file"))) if not (only_missing and user_file_exists and app_file_exists): _open_url(mastodon.auth_request_url(), echo=prompt) prompt = False code = click.prompt("Enter code for {}".format( section.get("api_base_url"))) mastodon.log_in(code=code, to_file=config.file( section.get("user_secret_file")))
def signin(): user_id, domain, uuid = get_signin_params() if user_id is '' or domain is '' or uuid is '': flash('情報の取得に失敗しました(user_id, domain, uuid)', 'error') return redirect(redirect_url, code=302) if get_login_user() is not None: flash('参加しています!', 'info') return redirect(redirect_url, code=302) save_uuid(user_id, domain, uuid) client_id, client_secret = get_oauth_applications(domain) if client_id is '' or client_secret is '': flash('情報の取得に失敗しました(cilent_id, client_secret)', 'error') return redirect(redirect_url, code=302) api_base_url = get_api_base_url(domain) mastodon = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=api_base_url) auth_url = mastodon.auth_request_url(client_id=client_id, redirect_uris=redirect_url, scopes=scopes) if auth_url is '': flash('情報の取得に失敗しました(auth_url)', 'error') return redirect(redirect_url, code=302) return redirect(auth_url, code=302)
def register(self): import webbrowser # get user handle handle = self.context.params.get(self.service_name, 'handle') api_base_url = handle.split('@')[-1] # get client data client_id = self.context.params.get(self.service_name, 'client_id') client_secret = self.context.params.get(self.service_name, 'client_secret') if (not client_id) or (not client_secret): client_id, client_secret = Mastodon.create_app( 'pywws', scopes=['write'], api_base_url=api_base_url) self.context.params.set(self.service_name, 'client_id', client_id) self.context.params.set(self.service_name, 'client_secret', client_secret) # create api api = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=api_base_url) # authorise auth_request_url = api.auth_request_url(scopes=['write']) if not webbrowser.open(auth_request_url, new=2, autoraise=0): print('Please use a web browser to open the following URL') print(auth_request_url) if sys.version_info[0] >= 3: input_ = input else: input_ = raw_input code = input_('Please enter the auth code shown in your web browser: ') code = code.strip() # log in access_token = api.log_in(code=code, scopes=['write']) self.context.params.set(self.service_name, 'access_token', access_token)
def authenticate(base_url=None, client_id=None, client_secret=None): if base_url is None: base_url = (input('Instance base URL [https://mastodon.social]: ') or 'https://mastodon.social') if client_id is None: client_id = input('Enter your client id: ') if client_secret is None: client_secret = input('Enter your client secret: ') api_unauth = Mastodon(api_base_url=base_url, client_id=client_id, client_secret=client_secret) print( 'Visit the following link, authenticate, and bring back the code that the page gives you afterwards.' ) print('NB: that code is *not* your access token.') print(api_unauth.auth_request_url(scopes=['read', 'write'])) code = input('> ') access_token = api_unauth.log_in(code=code, scopes=['read', 'write']) print('Got access token:', access_token) print('Testing...') api_auth = Mastodon(api_base_url=base_url, client_id=client_id, client_secret=client_secret, access_token=access_token) creds = api_auth.account_verify_credentials() print('Success, authenticated as {}.'.format(creds['username']))
def register(self): import webbrowser self.check_params('handle') api_base_url = self.params['handle'].split('@')[-1] # get client data client_id = self.context.params.get(service_name, 'client_id') client_secret = self.context.params.get(service_name, 'client_secret') if (not client_id) or (not client_secret): client_id, client_secret = Mastodon.create_app( 'pywws', scopes=['write'], api_base_url=api_base_url) self.context.params.set(service_name, 'client_id', client_id) self.context.params.set(service_name, 'client_secret', client_secret) # create api api = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=api_base_url) # authorise auth_request_url = api.auth_request_url(scopes=['write']) if not webbrowser.open(auth_request_url, new=2, autoraise=0): print('Please use a web browser to open the following URL') print(auth_request_url) if sys.version_info[0] >= 3: input_ = input else: input_ = raw_input code = input_('Please enter the auth code shown in your web browser: ') code = code.strip() # log in access_token = api.log_in(code=code, scopes=['write']) self.context.params.set(service_name, 'access_token', access_token)
def login(request): ms = Mastodon(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, api_base_url=API_BASE_URL) auth_request_url = ms.auth_request_url(redirect_uris=os.path.join(ROOT_URL, 'auth')) context = { 'service_name': SERVICE_NAME, 'auth_request_url': auth_request_url, } return render(request, 'app/login.html', context)
def callback_url(self, request): us = UserService.objects.get(user=request.user, name='ServiceMastodon') mastodon = MastodonAPI(client_id=us.client_id, client_secret=us.client_secret, access_token=us.token, api_base_url=us.host) redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(), reverse('mastodon_callback')) return mastodon.auth_request_url(redirect_uris=redirect_uris)
def callback_url(self, request): us = UserService.objects.get(user=request.user, name='ServiceMastodon') mastodon = MastodonAPI( client_id=us.client_id, client_secret=us.client_secret, access_token=us.token, api_base_url=us.host ) redirect_uris = '%s://%s%s' % (request.scheme, request.get_host(), reverse('mastodon_callback')) return mastodon.auth_request_url(redirect_uris=redirect_uris)
def authorize(self): """ Tries to authorize via OAuth API, and save access token. If it fails fallsback to username and password. """ url = self.url client_secret = self.client_secret user_secret = self.user_secret scopes = self.scopes print("This app needs access to your Mastodon account.") mastodon = Mastodon(client_id=client_secret, api_base_url=url) url = mastodon.auth_request_url(client_id=client_secret, scopes=scopes) print("Visit the following URL and authorize the app:") print(url) print("Then paste the access token here:") token = sys.stdin.readline().rstrip() try: # on the very first login, --pace has no effect mastodon.log_in(code=token, to_file=user_secret, scopes=scopes) except Exception: print( "Sadly, that did not work. On some sites, this login mechanism" ) print("(namely OAuth) seems to be broken. There is an alternative") print( "if you are willing to trust us with your password just this") print("once. We need it just this once in order to get an access") print( "token. We won't save it. If you don't want to try this, use") print("Ctrl+C to quit. If you want to try it, please provide your") print("login details.") sys.stdout.write("Email: ") sys.stdout.flush() email = sys.stdin.readline().rstrip() sys.stdout.write("Password: ") sys.stdout.flush() password = sys.stdin.readline().rstrip() # on the very first login, --pace has no effect mastodon.log_in(username=email, password=password, to_file=user_secret, scopes=scopes) return mastodon
def post(self, request, *args, **kwargs): config = Config.load() # Determine callback URL from passed location. location = urlparse(request.POST["location"]) redirect_location = ( location.scheme, location.netloc, reverse("nabmastodond.oauthcb"), "", "", "", ) redirect_uri = urlunparse(redirect_location) if ( config.instance != request.POST["instance"] or config.redirect_uri != redirect_uri ): config.client_id = None config.client_secret = None config.redirect_uri = None config.instance = request.POST["instance"] # Register application on Mastodon if config.client_secret is None: try: (client_id, client_secret) = Mastodon.create_app( "nabmastodond", api_base_url="https://" + config.instance, redirect_uris=redirect_uri, ) config.client_id = client_id config.client_secret = client_secret config.redirect_uri = redirect_uri reset_access_token(config) except MastodonError as e: return HttpResponse( "Unknown error", content=f'{{"status":"error",' f'"code":"MastodonError",' f'"message":"{e}"}}', mimetype="application/json", status=500, ) # Start OAuth process mastodon_client = Mastodon( client_id=config.client_id, client_secret=config.client_secret, api_base_url="https://" + config.instance, ) request_url = mastodon_client.auth_request_url( redirect_uris=redirect_uri ) return JsonResponse({"status": "ok", "request_url": request_url})
def login(request): # User posts instance name in form. # POST page redirects user to instance, where they log in. # Instance redirects user to oauth_after_login view. # oauth_after_login view saves credential in session, then redirects to home. if request.method == "GET": form = OAuthLoginForm() return render(request, 'setup/login-oauth.html', {'form': form}) elif request.method == "POST": form = OAuthLoginForm(request.POST) redirect_uris = request.build_absolute_uri(reverse('oauth_callback')) if form.is_valid(): api_base_url = form.cleaned_data['instance'] tmp_base = parse.urlparse(api_base_url.lower()) if tmp_base.netloc == '': api_base_url = parse.urlunparse( ('https', tmp_base.path, '', '', '', '')) request.session['instance_hostname'] = tmp_base.path else: api_base_url = api_base_url.lower() request.session['instance_hostname'] = tmp_base.netloc request.session['instance'] = api_base_url try: client = Client.objects.get(api_base_id=api_base_url) except (Client.DoesNotExist, Client.MultipleObjectsReturned): (client_id, client_secret) = Mastodon.create_app( 'brutaldon', api_base_url=api_base_url, redirect_uris=redirect_uris, scopes=['read', 'write', 'follow']) client = Client(api_base_id=api_base_url, client_id=client_id, client_secret=client_secret) client.save() request.session['client_id'] = client.client_id request.session['client_secret'] = client.client_secret mastodon = Mastodon(client_id=client.client_id, client_secret=client.client_secret, api_base_url=api_base_url) return redirect( mastodon.auth_request_url(redirect_uris=redirect_uris, scopes=['read', 'write', 'follow'])) else: return render(request, 'setup/login.html', {'form': form}) else: return redirect(login)
def login(instance, client_id, client_secret): """ Login to a Mastodon instance. Returns a valid Mastodon token if success, likely raises a Mastodon exception otherwise. """ # temporary object to aquire the token mastodon = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url="https://" + instance) print("Click the link to authorize login.") print(mastodon.auth_request_url()) print() code = input("Enter the code you received >") return mastodon.log_in(code=code)
def post(self, request, *args, **kwargs): config = Config.load() # Determine callback URL from passed location. location = urlparse(request.POST['location']) redirect_location = (location.scheme, location.netloc, reverse('nabmastodond.oauthcb'), '', '', '') redirect_uri = urlunparse(redirect_location) if config.instance != request.POST[ 'instance'] or config.redirect_uri != redirect_uri: config.client_id = None config.client_secret = None config.redirect_uri = None config.instance = request.POST['instance'] # Register application on Mastodon if config.client_secret == None: try: (client_id, client_secret) = Mastodon.create_app( 'nabmastodond', api_base_url='https://' + config.instance, redirect_uris=redirect_uri, ) config.client_id = client_id config.client_secret = client_secret config.redirect_uri = redirect_uri reset_access_token(config) except MastodonError as e: return HttpResponse( 'Unknown error', content= '{{"status":"error","code":"MastodonError","message":"{e}"}}' .format(e=e), mimetype='application/json', status=500) # Start OAuth process mastodon_client = Mastodon(client_id=config.client_id, client_secret=config.client_secret, api_base_url='https://' + config.instance) request_url = mastodon_client.auth_request_url( redirect_uris=redirect_uri) return JsonResponse({'status': 'ok', 'request_url': request_url})
def start_register(self, server): try: (id,secret)=Mastodon.create_app( client_name='mastaj xmpp gateway', api_base_url="https://"+server ) m=Mastodon( client_id=id, client_secret=secret, api_base_url="https://"+server ) url=m.auth_request_url( client_id=id, scopes=['read','write','follow'], redirect_uris='urn:ietf:wg:oauth:2.0:oob' ) self.mastodon=m self.mastodon_id=server print(url) return url except MastodonNetworkError: raise NetworkError()
api_base_url=cfg['site'], scopes=scopes, website="https://github.com/Lynnesbian/mstdn-ebooks") cfg['client'] = { "id": client_id, "secret": client_secret } if "secret" not in cfg: print("No user credentials, logging in") client = Mastodon(client_id = cfg['client']['id'], client_secret = cfg['client']['secret'], api_base_url=cfg['site']) print("Open this URL: {}".format(client.auth_request_url(scopes=scopes))) cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes) json.dump(cfg, open("config.json", "w+")) def extract_toot(toot): toot = toot.replace("'", "'") toot = toot.replace(""", '"') soup = BeautifulSoup(toot, "html.parser") # this is the code that removes all mentions # TODO: make it so that it removes the @ and instance but keeps the name for mention in soup.select("span.h-card"): mention.a.unwrap() mention.span.unwrap()
"mstdn-ebooks", api_base_url=cfg['site'], scopes=scopes, website="https://github.com/Lynnesbian/mstdn-ebooks") cfg['client'] = {"id": client_id, "secret": client_secret} if "secret" not in cfg: print("No user credentials -- logging in to {}".format(cfg['site'])) client = Mastodon(client_id=cfg['client']['id'], client_secret=cfg['client']['secret'], api_base_url=cfg['site']) print( "Open this URL and authenticate to give mstdn-ebooks access to your bot's account: {}" .format(client.auth_request_url(scopes=scopes))) cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes) json.dump(cfg, open("config.json", "w+")) def extract_toot(toot): toot = functions.extract_toot(toot) toot = toot.replace( "@", "@\u200B") #put a zws between @ and username to avoid mentioning return (toot) client = Mastodon(client_id=cfg['client']['id'], client_secret=cfg['client']['secret'], access_token=cfg['secret'],
scopes=scopes, website=meta['source_url']) # save application information cfg['client'] = { "id": client_id, "secret": client_secret } if "secret" not in cfg: print("No user credentials -- logging in to {}".format(cfg['site'])) client = Mastodon(client_id = cfg['client']['id'], client_secret = cfg['client']['secret'], api_base_url=cfg['site']) print("Open this URL and authenticate to give {} access to your bot's account: {}".format(meta['bot_name'], client.auth_request_url(scopes=scopes))) # log in and save the provided information cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes) # save the current configuration json.dump(cfg, open("config.json", "w+")) # test login information client = Mastodon( client_id=cfg['client']['id'], client_secret = cfg['client']['secret'], access_token=cfg['secret'], api_base_url=cfg['site']) me = client.account_verify_credentials()
if not path.exists(clientcred_secret): print("No clientcred.secret, registering application") Mastodon.create_app("ebooks", api_base_url=api_base_url, to_file=clientcred_secret, scopes=scopes) if not path.exists(usercred_secret): print("No usercred.secret, registering application") # email = input("Email: ") # password = getpass("Password: "******"clientcred.secret", api_base_url=api_base_url) # client.log_in(email, password, to_file="usercred.secret") print("Visit this url:") print(client.auth_request_url(scopes=scopes)) client.log_in(code=input("Secret: "), to_file=usercred_secret, scopes=scopes) def parse_toot(toot): if toot.spoiler_text != "": return if toot.reblog is not None: return if toot.visibility not in ["public", "unlisted"]: return soup = BeautifulSoup(toot.content, "html.parser") # pull the mentions out # for mention in soup.select("span.h-card"): # mention.unwrap()
def backup(args): """ Backup toots, followers, etc from your Mastodon account """ (username, domain) = args.user.split("@") url = 'https://' + domain client_secret = domain + '.client.secret' user_secret = domain + '.user.' + username + '.secret' status_file = domain + '.user.' + username + '.json' data = None if os.path.isfile(status_file): print("Loading existing backup") with open(status_file, mode = 'r', encoding = 'utf-8') as fp: data = json.load(fp) if not os.path.isfile(client_secret): print("Registering app") Mastodon.create_app( 'mastodon-backup', api_base_url = url, to_file = client_secret) if not os.path.isfile(user_secret): print("Log in") mastodon = Mastodon( client_id = client_secret, api_base_url = url) url = mastodon.auth_request_url( client_id = client_secret, scopes=['read']) print("Visit the following URL and authorize the app:") print(url) print("Then paste the access token here:") token = sys.stdin.readline().rstrip() mastodon.log_in( username = username, code = token, to_file = user_secret, scopes=['read']) else: mastodon = Mastodon( client_id = client_secret, access_token = user_secret, api_base_url = url) print("Get user info") user = mastodon.account_verify_credentials() def find_id(list, id): """Return the list item whose id attribute matches.""" return next((item for item in list if item["id"] == id), None) def fetch_up_to(page, id): statuses = [] # use a generator expression to find our last status found = find_id(page, id) # get the remaining pages while len(page) > 0 and found is None: statuses.extend(page) sys.stdout.flush() page = mastodon.fetch_next(page) if page is None: break found = find_id(page, id) page = page[0:page.index(found)] statuses.extend(page) print("Fetched a total of %d new toots" % len(statuses)) return statuses if data is None or not "statuses" in data: print("Get statuses (this may take a while)") statuses = mastodon.account_statuses(user["id"]) statuses = mastodon.fetch_remaining( first_page = statuses) else: id = data["statuses"][0]["id"] print("Get new statuses") statuses = fetch_up_to(mastodon.account_statuses(user["id"]), id) statuses.extend(data["statuses"]) if data is None or not "favourites" in data: print("Get favourites (this may take a while)") favourites = mastodon.favourites() favourites = mastodon.fetch_remaining( first_page = favourites) else: id = data["favourites"][0]["id"] print("Get new favourites") favourites = fetch_up_to(mastodon.favourites(), id) favourites.extend(data["favourites"]) data = { 'account': user, 'statuses': statuses, 'favourites': favourites } print("Saving %d statuses and %d favourites" % ( len(statuses), len(favourites))) date_handler = lambda obj: ( obj.isoformat() if isinstance(obj, (datetime.datetime, datetime.date)) else None) with open(status_file, mode = 'w', encoding = 'utf-8') as fp: data = json.dump(data, fp, indent = 2, default = date_handler)
print( "FediBooks needs access to an account to notify users when they've been added to bots." ) print("What instance would you like FediBooks' account to be on?") instance = input("https://") client_id, client_secret = Mastodon.create_app( "FediBooks", api_base_url="https://{}".format(instance), scopes=scopes, website=cfg['base_uri']) client = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url="https://{}".format(instance)) url = client.auth_request_url(client_id=client_id, scopes=scopes) print( "Create an account on {}, then click this link to give FediBooks access to the account: {}" .format(instance, url)) print("Authorise FediBooks to access the account, then paste the code below.") code = input("Code: ") print("Authenticating...") secret = client.log_in(code=code, scopes=scopes) client.status_post( "FediBooks has successfully been set up to use this account.") cfg['account'] = { 'client_id': client_id, 'client_secret': client_secret,
api_base_url=cfg['site'], scopes=scopes, website="https://github.com/Lynnesbian/mstdn-ebooks") cfg['client'] = { "id": client_id, "secret": client_secret } if "secret" not in cfg: print("No user credentials -- logging in to {}".format(cfg['site'])) client = Mastodon(client_id = cfg['client']['id'], client_secret = cfg['client']['secret'], api_base_url=cfg['site']) print("Open this URL and authenticate to give mstdn-ebooks access to your bot's account: {}".format(client.auth_request_url(scopes=scopes))) cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes) json.dump(cfg, open("config.json", "w+")) def extract_toot(toot): toot = functions.extract_toot(toot) toot = toot.replace("@", "@\u200B") #put a zws between @ and username to avoid mentioning return(toot) client = Mastodon( client_id=cfg['client']['id'], client_secret = cfg['client']['secret'], access_token=cfg['secret'], api_base_url=cfg['site'])
def bot_accounts_add(mysql, cfg): 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['handle'] == session['bot']: error = "Bots cannot learn from themselves." return render_template("bot/accounts_add.html", error=error) # look up user handle_list = session['handle'].split('@') if len(handle_list) != 3: # not formatted correctly error = "Incorrectly formatted handle." return render_template("bot/accounts_add.html", error=error) session['username'] = handle_list[1] session['instance'] = handle_list[2] if session['instance'] in json.load(open("blacklist.json")): session[ 'error'] = "Learning from accounts on this instance is not allowed." return redirect(url_for("render_bot_accounts_add")) try: r = requests.get("https://{}/api/v1/instance".format( session['instance']), timeout=10) except requests.exceptions.ConnectionError: error = "Couldn't connect to {}.".format(session['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 "Pleroma" in j['version']: session['instance_type'] = "Pleroma" session['step'] += 1 else: if 'contact_account' in j and 'is_pro' in j[ 'contact_account']: # gab instance session['error'] = "Gab instances are not supported." return render_template("bot/accounts_add.html", error=error) else: session['instance_type'] = "Mastodon" session['step'] += 1 else: error = "Unsupported instance type. Misskey support is planned." return render_template("bot/accounts_add.html", error=error) session['client_id'], session[ 'client_secret'] = Mastodon.create_app( "FediBooks User Authenticator", api_base_url="https://{}".format(session['instance']), scopes=["read:statuses", "read:accounts"] if session['instance_type'] == 'Mastodon' else ["read"], website=cfg['base_uri']) client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url="https://{}".format( session['instance'])) session['url'] = client.auth_request_url( client_id=session['client_id'], scopes=["read:statuses", "read:accounts"] if session['instance_type'] == 'Mastodon' else ["read"]) elif session['step'] == 2: # test authentication try: client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance']) session['secret'] = client.log_in( code=request.form['code'], scopes=["read:statuses", "read:accounts"] if session['instance_type'] == 'Mastodon' else ["read"], ) username = client.account_verify_credentials()['username'] if username != session['username']: error = "Please authenticate as {}.".format( session['username']) if username.lower() == session['username'].lower(): error += " Make sure you capitalised the name properly - @user and @USER are different." return render_template("bot/accounts_add.html", error=error) except: session['step'] = 1 error = "Authentication failed." 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( session['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(session['username'], session['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(outbox, headers={ "Accept": "application/json,application/activity+json" }, timeout=10) if r.status_code == 200: # success!! c = mysql.connection.cursor() c.execute( "INSERT IGNORE 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.close() mysql.connection.commit() 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( session['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))
def bot_create(mysql, cfg, scopes, scopes_pleroma): if request.method == 'POST': if session['step'] == 1: # strip leading https://, if provided session['instance'] = re.match(r"^(?:https?:\/\/)?(.*)", request.form['instance']).group(1) if session['instance'] in json.load(open("blacklist.json")): session[ 'error'] = "Creating a bot on this instance is not allowed." return redirect(url_for("render_bot_create")) # check for mastodon/pleroma try: r = requests.get("https://{}/api/v1/instance".format( session['instance']), timeout=10) except requests.ConnectionError: session['error'] = "Couldn't connect to https://{}.".format( session['instance']) return render_template("bot/create.html", error=session.pop('error', None)) except: session[ 'error'] = "An unknown error occurred while trying to load https://{}".format( session['instance']) return render_template("bot/create.html", error=session.pop('error', None)) if r.status_code == 200: j = r.json() if "Pleroma" in j['version']: session['instance_type'] = "Pleroma" session['step'] += 1 else: if 'contact_account' in j and 'is_pro' in j[ 'contact_account']: # gab instance session['error'] = "Gab instances are not supported." else: session['instance_type'] = "Mastodon" session['step'] += 1 else: # not a masto/pleroma instance # misskey is currently unsupported # all other instance types are also unsupported # return an error message #TODO: misskey session[ 'error'] = "Unsupported instance type. Misskey support is planned." elif session['step'] == 2: # nothing needs to be done here, this step just informs the user that their instance type is supported session['step'] += 1 elif session['step'] == 3: # authenticate with the given instance and obtain credentials if session['instance_type'] in ['Mastodon', 'Pleroma']: redirect_uri = '{}/do/authenticate_bot'.format(cfg['base_uri']) session['client_id'], session[ 'client_secret'] = Mastodon.create_app( "FediBooks", api_base_url="https://{}".format(session['instance']), scopes=scopes if session['instance_type'] == 'Mastodon' else scopes_pleroma, redirect_uris=[redirect_uri], website=cfg['base_uri']) client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url="https://{}".format( session['instance'])) url = client.auth_request_url( client_id=session['client_id'], redirect_uris=redirect_uri, scopes=scopes if session['instance_type'] == 'Mastodon' else scopes_pleroma) return redirect(url, code=303) elif session['instance_type'] == 'Misskey': # todo pass else: # the user clicked next on step 2 while having an unsupported instance type # take them back home del session['instance'] del session['instance_type'] session['step'] = 1 return redirect(url_for("home"), 303) else: if 'step' in session and session['step'] == 4: try: # test authentication client = Mastodon(client_id=session['client_id'], client_secret=session['client_secret'], api_base_url=session['instance']) session['secret'] = client.log_in( code=session['code'], scopes=scopes if session['instance_type'] == 'Mastodon' else scopes_pleroma, redirect_uri='{}/do/authenticate_bot'.format( cfg['base_uri'])) username = client.account_verify_credentials()['username'] handle = "@{}@{}".format(username, session['instance']) except: # authentication error occurred error = "Authentication failed." session['step'] = 3 return render_template("bot/create.html", error=error) c = mysql.connection.cursor() c.execute("SELECT COUNT(*) FROM bots WHERE handle = %s", (handle, )) count = c.fetchone() if count != None and count[0] == 1: session[ 'error'] = "{} is currently in use by another FediBooks bot.".format( handle) session['step'] = 1 return redirect(url_for("render_bot_create"), 303) # authentication success!! c.execute( "INSERT INTO `credentials` (client_id, client_secret, secret) VALUES (%s, %s, %s)", (session['client_id'], session['client_secret'], session['secret'])) credentials_id = c.lastrowid mysql.connection.commit() # get webpush url privated, publicd = client.push_subscription_generate_keys() private = privated['privkey'] public = publicd['pubkey'] secret = privated['auth'] client.push_subscription_set("{}/push/{}".format( cfg['base_uri'], handle), publicd, mention_events=True) c.execute( "INSERT INTO `bots` (handle, user_id, credentials_id, push_public_key, push_private_key, push_secret, instance_type) VALUES (%s, %s, %s, %s, %s, %s, %s)", (handle, session['user_id'], credentials_id, public, private, secret, session['instance_type'])) mysql.connection.commit() c.close() # clean up unneeded variables del session['code'] del session['instance'] del session['instance_type'] del session['client_id'] del session['client_secret'] else: # user is starting a new bot create request session['step'] = 1 return render_template("bot/create.html", error=session.pop('error', None))
def login(args, scopes = ['read']): """ Login to your Mastodon account """ pace = args.pace (username, domain) = args.user.split("@") url = 'https://' + domain client_secret = domain + '.client.secret' user_secret = domain + '.user.' + username + '.secret' mastodon = None if not os.path.isfile(client_secret): print("Registering app") Mastodon.create_app( 'mastodon-archive', api_base_url = url, to_file = client_secret) if not os.path.isfile(user_secret): print("Log in") mastodon = Mastodon( client_id = client_secret, api_base_url = url) url = mastodon.auth_request_url( client_id = client_secret, scopes=scopes) print("Visit the following URL and authorize the app:") print(url) print("Then paste the access token here:") token = sys.stdin.readline().rstrip() # on the very first login, --pace has no effect mastodon.log_in( username = username, code = token, to_file = user_secret, scopes=scopes) else: if pace: # in case the user kept running into a General API problem mastodon = Mastodon( client_id = client_secret, access_token = user_secret, api_base_url = url, ratelimit_method='pace', ratelimit_pacefactor=0.9, request_timeout=300) else: # the defaults are ratelimit_method='wait', # ratelimit_pacefactor=1.1, request_timeout=300 mastodon = Mastodon( client_id = client_secret, access_token = user_secret, api_base_url = url) return mastodon
def login(args, scopes=['read']): """ Login to your Mastodon account """ pace = hasattr(args, 'pace') and args.pace (username, domain) = args.user.split("@") url = 'https://' + domain client_secret = domain + '.client.secret' user_secret = domain + '.user.' + username + '.secret' mastodon = None if not os.path.isfile(client_secret): print("Registering app") Mastodon.create_app('mastodon-archive', api_base_url=url, scopes=scopes, to_file=client_secret) if not os.path.isfile(user_secret): print("This app needs access to your Mastodon account.") mastodon = Mastodon(client_id=client_secret, api_base_url=url) url = mastodon.auth_request_url(client_id=client_secret, scopes=scopes) print("Visit the following URL and authorize the app:") print(url) print("Then paste the access token here:") token = sys.stdin.readline().rstrip() try: # on the very first login, --pace has no effect mastodon.log_in(code=token, to_file=user_secret, scopes=scopes) except Exception as e: print( "Sadly, that did not work. On some sites, this login mechanism" ) print("(namely OAuth) seems to be broken. There is an alternative") print( "if you are willing to trust us with your password just this") print("once. We need it just this once in order to get an access") print( "token. We won't save it. If you don't want to try this, use") print("Ctrl+C to quit. If you want to try it, please provide your") print("login details.") sys.stdout.write("Email: ") sys.stdout.flush() email = sys.stdin.readline().rstrip() sys.stdout.write("Password: ") sys.stdout.flush() password = sys.stdin.readline().rstrip() # on the very first login, --pace has no effect mastodon.log_in(username=email, password=password, to_file=user_secret, scopes=scopes) else: if pace: # in case the user kept running into a General API problem mastodon = Mastodon(client_id=client_secret, access_token=user_secret, api_base_url=url, ratelimit_method='pace', ratelimit_pacefactor=0.9, request_timeout=300) else: # the defaults are ratelimit_method='wait', # ratelimit_pacefactor=1.1, request_timeout=300 mastodon = Mastodon(client_id=client_secret, access_token=user_secret, api_base_url=url) return mastodon