#! /usr/bin/env python3 # Create Pleroma App, a python script to generate OAuth tokens for fedi # Copyright (C) 2022 Anon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import getpass import re import sys import datetime import argparse import contextlib import os from pprint import pformat from mastodon import Mastodon, MastodonIllegalArgumentError, MastodonAPIError, MastodonIOError from collections import OrderedDict class CreateAppError(Exception): pass # Modify this section to suit your default preferances def default(key): defaults={ "app": "app", "domain": "https://yandere.cc", "scopes": "write", "encrypt": "y", "cfg": None, "long_date_format": "%m/%d/%Y %I:%M%p" } return defaults[key] # Function to format user input consistently with lables def _input(lable, ans=None): if ans: q = "{} (Default: {}): ".format( lable, ans) return input(q) or ans else: q = "{}: ".format(lable) return input(q) def get_client_id_and_secret(app, permissions, domain): try: return Mastodon.create_app(app, scopes=permissions, api_base_url=domain) except MastodonIOError: raise CreateAppError("An error occurred. Make sure the domain name is correct.") def get_api(client_id, client_secret, domain): api = Mastodon(client_id, client_secret, api_base_url=domain) return api def get_token(api, email, password, permissions): try: token = api.log_in(email, password, scopes=permissions) return token except MastodonIllegalArgumentError: raise CreateAppError("Username or Password is incorrect.") except MastodonAPIError: raise CreateAppError("Could not grant scopes:", ", ".join(permissions)) def get_cfg(cfg_name): try: import importlib cfg = importlib.import_module(cfg_name) return cfg except ImportError: raise CreateAppError("Cannot import module:", cfg_name, "Make sure you omitted the .py extension and try again") def package_settings(app, domain, client_id, client_secret, token): settings_server = OrderedDict([ ("app_name", app), ("api_base_url", domain), ("client_id", client_id), ("client_secret", client_secret), ("access_token", token) ]) return settings_server def get_setting_reminder(fmt): dt_now = datetime.datetime.now() dt_now = dt_now.replace(year=dt_now.year + 1) settings_reminder = dt_now.strftime(fmt) return settings_reminder def credentials_py(label, setting): return "{} = {}".format(label, pformat(setting)) def credentials_ini_fmt(k, v): return "{}={}".format(k, v) def credentials_ini(label, setting): r = "[{}]\n".format(label) dictitems = getattr(type(setting), "items", None) if dictitems and callable(dictitems): r += "\n".join([credentials_ini_fmt(k, v) for k, v in setting.items()]) else: r += credentials_ini_fmt(label, str(setting)) return r def main(): # Default time localization long_date_format = default("long_date_format") # Parser parser = argparse.ArgumentParser( description="A script to generate OAuth tokens for fedi", epilog="", add_help=True) parser.add_argument("-c", "--config", help="Use time localization settings from a pyhton config file (omit the .py extension)", default=None) parser.add_argument("--plain", help="Output credentials in plain ini format", action="store_true") parser.add_argument("--minimal", help="Only print OAuth credentials, ignoring encryption settings", action="store_true") arguments = parser.parse_args() try: # Custom time localization # This is mainly intended to be used with bots configured with python files # This python file should contain the following dictionary with a key value pair of: # settings_time = {"long_date_format": "%m/%d/%Y %I:%M%p"} load_config = arguments.config or default("cfg") if load_config: cfg = get_cfg(load_config) long_date_format = cfg.settings_time["long_date_format"] # Create App print("Generate and register Mastodon App") print("You can just hit enter to accept the default for prompts that have them") print("Ctrl+C to Quit\n") # Get instance information app = _input("Enter your app name", default("app")) domain = _input("URL of Instance", default("domain")) email = _input("Enter your email") password = getpass.getpass("Enter password: ") print("Scopes: read, write, follow, push") print("Separate each scope with a comma (example above).") print("!!! Accept the default unless you intend to modify Yandere Lewd Bot !!!") ans = _input("Scopes", default("scopes")) ans = re.sub(r"\s+", "", ans, flags=re.UNICODE) permissions = ans.split(",") print("Granting:", permissions) # Begin logging in client_id, client_secret = get_client_id_and_secret(app, permissions, domain) api = get_api(client_id, client_secret, domain) token = get_token(api, email, password, permissions) # Credentials (unencrypted) encrypt, salt = False, "" settings_server = package_settings(app, domain, client_id, client_secret, token) reminder = get_setting_reminder(long_date_format) # Encrypt if not arguments.minimal: with contextlib.suppress(ImportError): import encryption ans = _input("Do you want to encrypt your credentials? (y/n)", default("encrypt")) if ans.upper() in ("Y", "YES"): encrypt = True salt, settings_server = encryption.settings_server_encrypt_cfg(settings_server) settings_encrypt = OrderedDict([ ("encrypt", encrypt), ("salt", salt), ]) # Credential formatting functions formatted_credentials = credentials_ini if arguments.plain else credentials_py # Output the user's new credentials print("Copy to your config file!!!") print(formatted_credentials("settings_server", settings_server)) if not arguments.minimal: print("\n{}\n".format(formatted_credentials("settings_reminder", reminder))) print(formatted_credentials("settings_encrypt", settings_encrypt)) print("Success :)") return 0 except (KeyboardInterrupt, EOFError): print("User Quit :|") return 1 except CreateAppError as e: print(e) print("Error :(") return 2 except Exception as e: print(e) print("Unhandled Exception ¯\\_(ツ)_/¯") return 3 if __name__ == "__main__": sys.exit(main())