From 8e2f2908a640031c537cff0e39fe09ef253be20e Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 13 Dec 2018 01:28:23 +0200 Subject: [PATCH] Add stuff --- maubot/cli/__init__.py | 2 + maubot/cli/__main__.py | 0 maubot/cli/base.py | 23 ++++++++++ maubot/cli/commands/__init__.py | 1 + maubot/cli/commands/build.py | 31 +++++++++++++ maubot/cli/commands/init.py | 34 ++++++++++++++ maubot/cli/commands/login.py | 48 ++++++++++++++++++++ maubot/cli/commands/upload.py | 25 ++++++++++ maubot/cli/config.py | 36 +++++++++++++++ maubot/cli/template/config.yaml | 6 +++ maubot/cli/template/plugin-with-config.py.j2 | 20 ++++++++ maubot/cli/template/plugin.py.j2 | 8 ++++ maubot/cli/util/__init__.py | 1 + maubot/cli/util/path.py | 14 ++++++ requirements.txt | 4 ++ setup.py | 6 ++- 16 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 maubot/cli/__init__.py delete mode 100644 maubot/cli/__main__.py create mode 100644 maubot/cli/base.py create mode 100644 maubot/cli/commands/__init__.py create mode 100644 maubot/cli/commands/build.py create mode 100644 maubot/cli/commands/init.py create mode 100644 maubot/cli/commands/login.py create mode 100644 maubot/cli/commands/upload.py create mode 100644 maubot/cli/config.py create mode 100644 maubot/cli/template/config.yaml create mode 100644 maubot/cli/template/plugin-with-config.py.j2 create mode 100644 maubot/cli/template/plugin.py.j2 create mode 100644 maubot/cli/util/__init__.py create mode 100644 maubot/cli/util/path.py diff --git a/maubot/cli/__init__.py b/maubot/cli/__init__.py new file mode 100644 index 0000000..d25736b --- /dev/null +++ b/maubot/cli/__init__.py @@ -0,0 +1,2 @@ +from . import commands +from .base import app diff --git a/maubot/cli/__main__.py b/maubot/cli/__main__.py deleted file mode 100644 index e69de29..0000000 diff --git a/maubot/cli/base.py b/maubot/cli/base.py new file mode 100644 index 0000000..2201c30 --- /dev/null +++ b/maubot/cli/base.py @@ -0,0 +1,23 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +import click + +from .config import load_config + + +@click.group() +def app() -> None: + load_config() diff --git a/maubot/cli/commands/__init__.py b/maubot/cli/commands/__init__.py new file mode 100644 index 0000000..e94091c --- /dev/null +++ b/maubot/cli/commands/__init__.py @@ -0,0 +1 @@ +from . import upload, build, login, init diff --git a/maubot/cli/commands/build.py b/maubot/cli/commands/build.py new file mode 100644 index 0000000..b820e2c --- /dev/null +++ b/maubot/cli/commands/build.py @@ -0,0 +1,31 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +import click +import os + +from ..base import app +from ..util import type_path + + +@app.command(short_help="Build a maubot plugin", + help="Build a maubot plugin. First parameter is the path to root of the plugin " + "to build. You can also use --output to specify output file.") +@click.argument("path", default=".") +@click.option("-o", "--output", help="Path to output built plugin to", type=type_path) +@click.option("-u", "--upload", help="Upload plugin to main server after building", is_flag=True, + default=False) +def build(path: str, output: str, upload: bool) -> None: + pass diff --git a/maubot/cli/commands/init.py b/maubot/cli/commands/init.py new file mode 100644 index 0000000..d2bb633 --- /dev/null +++ b/maubot/cli/commands/init.py @@ -0,0 +1,34 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +import click +import os + +from ..base import app +from ..util import type_path + + +@app.command(help="Initialize a new maubot plugin") +@click.option("-n", "--name", help="The name of the project", default=os.path.basename(os.getcwd()), + prompt=True, show_default="directory name") +@click.option("-i", "--id", help="The maubot plugin ID (Java package name format)", prompt=True) +@click.option("-v", "--version", help="Initial version for project", default="0.1.0", + show_default=True) +@click.option("-l", "--license", help="The SPDX license identifier of the license for the project", + prompt=True, default="AGPL-3.0-or-later") +@click.option("-c", "--config", help="Include a config in the plugin stub", is_flag=True, + default=False) +def init(name: str, id: str, version: str, license: str, config: bool) -> None: + pass diff --git a/maubot/cli/commands/login.py b/maubot/cli/commands/login.py new file mode 100644 index 0000000..9f91bc1 --- /dev/null +++ b/maubot/cli/commands/login.py @@ -0,0 +1,48 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +from urllib.request import urlopen +from urllib.error import HTTPError +import click +import json +import os + +from colorama import Fore, Style + +from maubot.cli.base import app +from maubot.cli.config import save_config, config + + +@app.command(help="Log in to a Maubot instance") +@click.argument("server", required=True, default="http://localhost:29316") +@click.option("-u", "--username", help="The username of your account", prompt=True, + default=lambda: os.environ.get('USER', ''), show_default="current user") +@click.password_option("-p", "--password", help="The password to your account", required=True, + confirmation_prompt=False) +def login(server, username, password) -> None: + data = { + "username": username, + "password": password, + } + try: + with urlopen(f"{server}/_matrix/maubot/v1/auth/login", + data=json.dumps(data).encode("utf-8")) as resp_data: + resp = json.load(resp_data) + config["servers"][server] = resp["token"] + save_config() + print(Fore.GREEN, "Logged in successfully") + except HTTPError as e: + if e.code == 401: + print(Fore.RED + "Invalid username or password" + Style.RESET_ALL) diff --git a/maubot/cli/commands/upload.py b/maubot/cli/commands/upload.py new file mode 100644 index 0000000..c30a7a2 --- /dev/null +++ b/maubot/cli/commands/upload.py @@ -0,0 +1,25 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +import click +import os + +from maubot.cli.base import app + + +@app.command(help="Upload a maubot plugin") +@click.option("-s", "--server", help="The maubot instance to upload the plugin to") +def upload(server: str) -> None: + pass diff --git a/maubot/cli/config.py b/maubot/cli/config.py new file mode 100644 index 0000000..fba278a --- /dev/null +++ b/maubot/cli/config.py @@ -0,0 +1,36 @@ +# maubot - A plugin-based Matrix bot system. +# Copyright (C) 2018 Tulir Asokan +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +import json +import os + +config = { + "servers": {} +} +configdir = os.environ.get("XDG_CONFIG_HOME", os.path.join(os.environ.get("HOME"), ".config")) + + +def save_config() -> None: + with open(f"{configdir}/maubot-cli.json", "w") as file: + json.dump(config, file) + + +def load_config() -> None: + try: + with open(f"{configdir}/maubot-cli.json") as file: + loaded = json.load(file) + config["servers"] = loaded["servers"] + except FileNotFoundError: + pass diff --git a/maubot/cli/template/config.yaml b/maubot/cli/template/config.yaml new file mode 100644 index 0000000..bbeb6da --- /dev/null +++ b/maubot/cli/template/config.yaml @@ -0,0 +1,6 @@ +example_1: Example value 1 +example_2: + list: + - foo + - bar + value: asd diff --git a/maubot/cli/template/plugin-with-config.py.j2 b/maubot/cli/template/plugin-with-config.py.j2 new file mode 100644 index 0000000..18ca827 --- /dev/null +++ b/maubot/cli/template/plugin-with-config.py.j2 @@ -0,0 +1,20 @@ +from maubot import Plugin +from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper + +class Config(BaseProxyConfig): + def do_update(self, helper: ConfigUpdateHelper) -> None: + helper.copy("example_1") + helper.copy("example_2.list") + helper.copy("example_2.value") + +class {{ name }}: + async def start() -> None: + self.config.load_and_update() + self.log.debug("Loaded %s from config example 2", self.config["example_2.value"]) + + async def stop() -> None: + pass + + @classmethod + def get_config_class(cls) -> Type[BaseProxyConfig]: + return Config diff --git a/maubot/cli/template/plugin.py.j2 b/maubot/cli/template/plugin.py.j2 new file mode 100644 index 0000000..fc056ff --- /dev/null +++ b/maubot/cli/template/plugin.py.j2 @@ -0,0 +1,8 @@ +from maubot import Plugin + +class {{ name }}: + async def start() -> None: + pass + + async def stop() -> None: + pass diff --git a/maubot/cli/util/__init__.py b/maubot/cli/util/__init__.py new file mode 100644 index 0000000..a5cbafb --- /dev/null +++ b/maubot/cli/util/__init__.py @@ -0,0 +1 @@ +from .path import type_path diff --git a/maubot/cli/util/path.py b/maubot/cli/util/path.py new file mode 100644 index 0000000..75cae57 --- /dev/null +++ b/maubot/cli/util/path.py @@ -0,0 +1,14 @@ +import click +import os + + +def type_path(val: str) -> str: + val = os.path.abspath(val) + if os.path.exists(val): + return val + directory = os.path.dirname(val) + if not os.path.isdir(directory): + if os.path.exists(directory): + raise click.BadParameter(f"{directory} is not a directory") + raise click.BadParameter(f"{directory} does not exist") + return val diff --git a/requirements.txt b/requirements.txt index 4067af6..79baba1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,7 @@ ruamel.yaml attrs bcrypt packaging + +click +colorama +jinja2 diff --git a/setup.py b/setup.py index a8c79db..2dc5ae3 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,10 @@ setuptools.setup( "attrs>=18.1.0,<19", "bcrypt>=3.1.4,<4", "packaging>=10", + + "click>=7,<8", + "colorama>=0.4,<0.5", + "jinja2>=2,<3", ], classifiers=[ @@ -45,7 +49,7 @@ setuptools.setup( entry_points=""" [console_scripts] maubot=maubot.__main__:main - mbp=maubot.cli.__main__:main + mbc=maubot.cli:app """, data_files=[ (".", ["example-config.yaml"]),