diff --git a/create_app.py b/create_app.py new file mode 100755 index 0000000..eb709f5 --- /dev/null +++ b/create_app.py @@ -0,0 +1,154 @@ +#! /usr/bin/env python3 + +# CreatePleromaApp, a python script to generate OAuth tokens for fedi +# Copyright (C) 2024 Anon <@Anon@yandere.cc> +# +# 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 os +import re +import sys +import argparse +from mastodon import Mastodon, MastodonIllegalArgumentError, MastodonAPIError, MastodonIOError + + +class CreateAppError(Exception): + pass + + +# Modify this section to suit your default preferances +def default(key): + defaults={ + "app": "app", + "domain": "https://yandere.cc", + "scopes": "write", + } + return defaults[key] + + +def printerr(s, end=None): + print(s, file=sys.stderr, end=end if end != None else os.linesep) + +def inputerr(q): + sys.stdout.flush() + sys.stderr.flush() + printerr(q, end="") + return input() + +# Function to format user input consistently with lables +def _input(lable, ans=""): + if ans: + q = "{} (Default: {}): ".format( + lable, ans) + return inputerr(q) or ans + else: + q = "{}: ".format(lable) + return inputerr(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, + feature_set="pleroma" + ) + 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 main(): + # Parser + parser = argparse.ArgumentParser( + description="A script to generate OAuth tokens for fedi", + epilog="", + add_help=True) + _ = parser.parse_args() + + try: + # Settings Dictionary + settings = {} + + # Create App + printerr("Generate and register Mastodon App") + printerr("You can just hit enter to accept the default for prompts that have them") + printerr("Ctrl+C to Quit\n") + + # Get instance information + app_name = _input("Enter your app name", default("app")) + settings["API_BASE_URL"] = _input("URL of Instance", default("domain")) + email = _input("Enter your email") + password = getpass.getpass("Enter password: ") + printerr("Scopes: read, write, follow, push") + printerr("Separate each scope with a comma (example above).") + printerr("!!! 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(",") + printerr("Granting: {}".format(str(permissions))) + + # Begin logging in + settings["CLIENT_ID"], settings["CLIENT_SECRET"] =\ + get_client_id_and_secret( + app_name, + permissions, + settings["API_BASE_URL"] + ) + api = get_api( + settings["CLIENT_ID"], + settings["CLIENT_SECRET"], + settings["API_BASE_URL"] + ) + + settings["ACCESS_TOKEN"] = get_token(api, email, password, permissions) + + # Output the user's new credentials + for k, v in settings.items(): + v = settings[k] + print("{}={}".format(k, v)) + + printerr("Success :)") + return 0 + except (KeyboardInterrupt, EOFError): + printerr("\nUser Quit :|") + return 1 + except CreateAppError as e: + printerr(e) + printerr("Error :(") + return 2 + except Exception as e: + printerr(e) + printerr("Unhandled Exception ¯\\_(ツ)_/¯") + return 3 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f9fc8bb..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Mastodon.py -cryptography \ No newline at end of file diff --git a/run.sh b/run.sh index c77a466..e381ef3 100755 --- a/run.sh +++ b/run.sh @@ -1,7 +1,7 @@ #! /usr/bin/env bash -# Create Pleroma App, automate the creation of pleroma apps -# Copyright (C) 2022 Anon <@Anon@yandere.cc> +# CreatePleromaApp, a python script to generate OAuth tokens for fedi +# Copyright (C) 2024 Anon <@Anon@yandere.cc> # # 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 @@ -21,8 +21,8 @@ ABS_PATH="$(readlink -f "$0")" RUN_DIR="$(dirname "$ABS_PATH")" # Relative paths to the virtual environment and main.py -VENV='./venv/bin/activate' -ENTRY='./src/create_app.py' +VENV='${RUN_DIR}/venv/bin/activate' +ENTRY='${RUN_DIR}/create_app.py' # cd into the bot's root path, set up the virtual environment, and run cd "$RUN_DIR" diff --git a/src/create_app.py b/src/create_app.py deleted file mode 100755 index 646f875..0000000 --- a/src/create_app.py +++ /dev/null @@ -1,216 +0,0 @@ -#! /usr/bin/env python3 - -# Create Pleroma App, a python script to generate OAuth tokens for fedi -# Copyright (C) 2022 Anon <@Anon@yandere.cc> -# -# 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(label, setting): - r = "[{}]".format(label) - for k, v in setting.items(): - r += "\n{}={}".format(k, v) - 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("-k", "--keyfile", help="Keyfile used for decryption", 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 FediBotEncryption - ans = _input("Do you want to encrypt your credentials? (y/n)", default("encrypt")) - if ans.upper() in ("Y", "YES"): - encrypt = True - salt, settings_server = FediBotEncryption.settings_server_encrypt(settings_server, arguments.keyfile) - - settings_encrypt = OrderedDict([ - ("encrypt", encrypt), - ("salt", salt), - ("keyfile", arguments.keyfile) - ]) - settings_reminder = OrderedDict([ - ("settings_reminder", reminder) - ]) - - # 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", settings_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())