diff --git a/cleantoots/config.py b/cleantoots/config.py new file mode 100644 index 0000000..86fac12 --- /dev/null +++ b/cleantoots/config.py @@ -0,0 +1,120 @@ +import configparser +import os +import sys + +import click +from mastodon import Mastodon + + +@click.group() +@click.pass_obj +def config(config): + """Manage cleantoot's config.""" + pass + + +@config.command() +@click.pass_obj +def setup(config): + """Initial setup for configuration directories and files.""" + os.makedirs(config.dir, exist_ok=True) + if os.path.isfile(config.main_file): + click.secho( + "{} found. Not touching anything.".format(config.main_file), fg="yellow" + ) + command = click.style("cleantoots config edit", bold=True) + click.echo("You may want to edit the file. Use: {}.".format(command)) + return + + default_config = configparser.ConfigParser() + default_config["DEFAULT"] = { + "boost_limit": 5, + "favorite_limit": 5, + "days_count": 30, + "timezone": "Europe/Paris", + } + default_config["Mastodon.social"] = { + "api_base_url": "https://mastodon.social", + "app_secret_file": "mastodon_social_app.secret", + "user_secret_file": "mastodon_social_user.secret", + "protected_toots": "1234\n5678", + } + with open(config.main_file, "w") as _file: + default_config.write(_file) + click.secho("{} written.".format(config.main_file), fg="green") + click.echo() + click.secho("Next steps", bold=True) + click.echo( + "You'll need to edit the config file in order to set some settings such as:" + ) + click.echo("* The base URL of your Mastodon instance") + click.echo("* The toots you want to protect") + if sys.stdout.isatty() and sys.stdin.isatty(): + click.echo() + click.secho("We're going to open the file for you now.") + click.pause() + click.edit(filename=config.main_file) + + +@config.command(name="list") +@click.pass_obj +def list_(config): + """Display parsed config.""" + if not config.sections(): + click.secho("The config file doesn't seem to have any section.", fg="yellow") + command = click.style("cleantoots config setup", bold=True) + click.secho("You should set it up first. Use: {}".format(command)) + return + for section_name in config.sections(): + click.secho(section_name, bold=True) + section = config[section_name] + for key, value in section.items(): + click.secho("{} = {}".format(key, value)) + click.echo() + + +@config.command() +@click.pass_obj +def edit(config): + """Edit config file.""" + if not config.sections(): + click.secho("The config file doesn't seem to have any section.", fg="yellow") + command = click.style("cleantoots config setup", bold=True) + click.secho("You should set it up first. Use: {}".format(command)) + return + if sys.stdout.isatty() and sys.stdin.isatty(): + click.edit(filename=config.main_file) + else: + click.secho("Not running in a terminal, can't open file.", fg="red") + + +@config.command() +@click.pass_obj +def login(config): + """Fetch credentials for each app described in config file.""" + for section in config.sections(): + section = config[section] + 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 sys.stdout.isatty() and sys.stdin.isatty(): + click.echo( + "We will now open a browser for each account set in the config file." + ) + click.echo( + "You'll need to authenticate and then copy the code provided in the web " + "page back into this terminal, upon prompt." + ) + click.pause() + click.launch(mastodon.auth_request_url()) + else: + click.echo( + "Go to {}, authenticate and enter the code below.".format( + mastodon.auth_request_url() + ) + ) + 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"))) diff --git a/cleantoots/main.py b/cleantoots/main.py index 1c040cf..5623e9c 100644 --- a/cleantoots/main.py +++ b/cleantoots/main.py @@ -1,13 +1,13 @@ import configparser import os import pathlib -import sys import click import pendulum -from click import Abort from mastodon import Mastodon +from cleantoots import config as config_commands + HOME = pathlib.Path.home() DEFAULT_CONFIG_DIR = click.get_app_dir("cleantoots") DEFAULT_CONFIG_FILENAME = "config.ini" @@ -43,6 +43,7 @@ class CleanTootsConfig(configparser.ConfigParser): default=DEFAULT_CONFIG_FILENAME, show_default=True, ) +@click.version_option() @click.pass_context def cli(ctx, config_dir, config_file): """ @@ -57,89 +58,7 @@ def cli(ctx, config_dir, config_file): ctx.obj = CleanTootsConfig(config_dir, config_file) -@cli.command() -@click.pass_obj -def setup_config(config): - """Initial setup for configuration directories and files.""" - os.makedirs(config.dir, exist_ok=True) - if os.path.isfile(config.main_file): - click.secho( - "{} found. Not touching anything.".format(config.main_file), fg="red" - ) - raise Abort() - - default_config = configparser.ConfigParser() - default_config["DEFAULT"] = { - "boost_limit": 5, - "favorite_limit": 5, - "days_count": 30, - "timezone": "Europe/Paris", - } - default_config["Mastodon.social"] = { - "api_base_url": "https://mastodon.social", - "app_secret_file": "mastodon_social_app.secret", - "user_secret_file": "mastodon_social_user.secret", - "protected_toots": "1234\n5678", - } - with open(config.main_file, "w") as _file: - default_config.write(_file) - click.secho("{} written.".format(config.main_file), fg="green") - click.echo() - click.secho("Next steps", bold=True) - click.echo( - "You'll need to edit the config file in order to set some settings such as:" - ) - click.echo("* The base URL of your Mastodon instance") - click.echo("* The toots you want to protect") - if sys.stdout.isatty() and sys.stdin.isatty(): - click.echo() - click.secho("We're going to open the file for you now.") - click.pause() - click.edit(filename=config.main_file) - - -@cli.command() -@click.pass_obj -def config(config): - """Display parsed config.""" - for section_name in config.sections(): - click.secho(section_name, bold=True) - section = config[section_name] - for key, value in section.items(): - click.secho("{} = {}".format(key, value)) - click.echo() - - -@cli.command() -@click.pass_obj -def login(config): - """Fetch credentials for each app described in config file.""" - for section in config.sections(): - section = config[section] - 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 sys.stdout.isatty() and sys.stdin.isatty(): - click.echo( - "We will now open a browser for each account set in the config file." - ) - click.echo( - "You'll need to authenticate and then copy the code provided in the web " - "page back into this terminal, upon prompt." - ) - click.pause() - click.launch(mastodon.auth_request_url()) - else: - click.echo( - "Go to {}, authenticate and enter the code below.".format( - mastodon.auth_request_url() - ) - ) - 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"))) +cli.add_command(config_commands.config) @cli.command() diff --git a/pyproject.toml b/pyproject.toml index 95081af..693d370 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cleantoots" -version = "0.2.3" +version = "0.3.0" description = "Cleanup your toot history." license = "GPL-3.0-or-later" authors = ["Gabriel Augendre "] diff --git a/tests/test_cli.py b/tests/test_cli.py index 216be29..e09c338 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -12,23 +12,24 @@ class SetupConfigTestCase(unittest.TestCase): def test_setup_config(self): with self.runner.isolated_filesystem(): - result = self.runner.invoke(cli, ["-d", ".", "setup-config"]) + result = self.runner.invoke(cli, ["-d", ".", "config", "setup"]) self.assertEqual(result.exit_code, 0) self.assertIn("config.ini written", result.output) self.assertTrue(os.path.isfile(os.path.join(".", "config.ini"))) def test_setup_config_twice_fails(self): with self.runner.isolated_filesystem(): - self.runner.invoke(cli, ["-d", ".", "setup-config"]) + self.runner.invoke(cli, ["-d", ".", "config", "setup"]) self.assertTrue(os.path.isfile(os.path.join(".", "config.ini"))) - result = self.runner.invoke(cli, ["-d", ".", "setup-config"]) - self.assertEqual(result.exit_code, 1) + result = self.runner.invoke(cli, ["-d", ".", "config", "setup"]) + self.assertEqual(result.exit_code, 0) self.assertIn("Not touching anything", result.output) + self.assertIn("cleantoots config edit", result.output) def test_config_output(self): with self.runner.isolated_filesystem(): - self.runner.invoke(cli, ["-d", ".", "setup-config"]) - result = self.runner.invoke(cli, ["-d", ".", "config"]) + self.runner.invoke(cli, ["-d", ".", "config", "setup"]) + result = self.runner.invoke(cli, ["-d", ".", "config", "list"]) expected = [ "Mastodon.social", "api_base_url", @@ -43,10 +44,24 @@ class SetupConfigTestCase(unittest.TestCase): for exp in expected: self.assertIn(exp, result.output) + def test_config_list_no_file(self): + with self.runner.isolated_filesystem(): + result = self.runner.invoke(cli, ["-d", ".", "config", "list"]) + self.assertIn("cleantoots config setup", result.output) + self.assertEqual(0, result.exit_code) + + def test_config_edit_no_file(self): + with self.runner.isolated_filesystem(): + result = self.runner.invoke(cli, ["-d", ".", "config", "edit"]) + self.assertIn("cleantoots config setup", result.output) + self.assertEqual(0, result.exit_code) + def test_login_output(self): with self.runner.isolated_filesystem(): - self.runner.invoke(cli, ["-d", ".", "setup-config"]) - result = self.runner.invoke(cli, ["-d", ".", "login"], input="\nFAKECODE") + self.runner.invoke(cli, ["-d", ".", "config", "setup"]) + result = self.runner.invoke( + cli, ["-d", ".", "config", "login"], input="\nFAKECODE" + ) self.assertIn("Enter code for", result.output)