Update rest of maubot-cli HTTP requests to asyncio
This commit is contained in:
parent
14d410739c
commit
e1a1f7b65e
@ -1,5 +1,5 @@
|
|||||||
# maubot - A plugin-based Matrix bot system.
|
# maubot - A plugin-based Matrix bot system.
|
||||||
# Copyright (C) 2019 Tulir Asokan
|
# Copyright (C) 2021 Tulir Asokan
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -13,8 +13,9 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from typing import Any, Callable, Union, Optional
|
from typing import Any, Callable, Union, Optional, Type
|
||||||
import functools
|
import functools
|
||||||
|
import traceback
|
||||||
import inspect
|
import inspect
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ import aiohttp
|
|||||||
|
|
||||||
from prompt_toolkit.validation import Validator
|
from prompt_toolkit.validation import Validator
|
||||||
from questionary import prompt
|
from questionary import prompt
|
||||||
|
from colorama import Fore
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from ..base import app
|
from ..base import app
|
||||||
@ -33,7 +35,10 @@ def with_http(func):
|
|||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
async with aiohttp.ClientSession() as sess:
|
async with aiohttp.ClientSession() as sess:
|
||||||
|
try:
|
||||||
return await func(*args, sess=sess, **kwargs)
|
return await func(*args, sess=sess, **kwargs)
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
print(f"{Fore.RED}Connection error: {e}{Fore.RESET}")
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@ -45,14 +50,17 @@ def with_authenticated_http(func):
|
|||||||
if not token:
|
if not token:
|
||||||
return
|
return
|
||||||
async with aiohttp.ClientSession(headers={"Authorization": f"Bearer {token}"}) as sess:
|
async with aiohttp.ClientSession(headers={"Authorization": f"Bearer {token}"}) as sess:
|
||||||
|
try:
|
||||||
return await func(*args, sess=sess, server=server, **kwargs)
|
return await func(*args, sess=sess, server=server, **kwargs)
|
||||||
|
except aiohttp.ClientError as e:
|
||||||
|
print(f"{Fore.RED}Connection error: {e}{Fore.RESET}")
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def command(help: str) -> Callable[[Callable], Callable]:
|
def command(help: str) -> Callable[[Callable], Callable]:
|
||||||
def decorator(func) -> Callable:
|
def decorator(func) -> Callable:
|
||||||
questions = func.__inquirer_questions__.copy()
|
questions = getattr(func, "__inquirer_questions__", {}).copy()
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
@ -79,9 +87,13 @@ def command(help: str) -> Callable[[Callable], Callable]:
|
|||||||
return
|
return
|
||||||
kwargs = {**kwargs, **resp}
|
kwargs = {**kwargs, **resp}
|
||||||
|
|
||||||
|
try:
|
||||||
res = func(*args, **kwargs)
|
res = func(*args, **kwargs)
|
||||||
if inspect.isawaitable(res):
|
if inspect.isawaitable(res):
|
||||||
asyncio.run(res)
|
asyncio.run(res)
|
||||||
|
except Exception:
|
||||||
|
print(Fore.RED + "Fatal error running command" + Fore.RESET)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
return app.command(help=help)(wrapper)
|
return app.command(help=help)(wrapper)
|
||||||
|
|
||||||
@ -104,12 +116,14 @@ yesno.__name__ = "yes/no"
|
|||||||
|
|
||||||
def option(short: str, long: str, message: str = None, help: str = None,
|
def option(short: str, long: str, message: str = None, help: str = None,
|
||||||
click_type: Union[str, Callable[[str], Any]] = None, inq_type: str = None,
|
click_type: Union[str, Callable[[str], Any]] = None, inq_type: str = None,
|
||||||
validator: Validator = None, required: bool = False, default: str = None,
|
validator: Type[Validator] = None, required: bool = False,
|
||||||
is_flag: bool = False, prompt: bool = True, required_unless: str = None
|
default: Union[str, bool, None] = None, is_flag: bool = False, prompt: bool = True,
|
||||||
) -> Callable[[Callable], Callable]:
|
required_unless: str = None) -> Callable[[Callable], Callable]:
|
||||||
if not message:
|
if not message:
|
||||||
message = long[2].upper() + long[3:]
|
message = long[2].upper() + long[3:]
|
||||||
click_type = validator.click_type if isinstance(validator, ClickValidator) else click_type
|
|
||||||
|
if isinstance(validator, type) and issubclass(validator, ClickValidator):
|
||||||
|
click_type = validator.click_type
|
||||||
if is_flag:
|
if is_flag:
|
||||||
click_type = yesno
|
click_type = yesno
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# maubot - A plugin-based Matrix bot system.
|
# maubot - A plugin-based Matrix bot system.
|
||||||
# Copyright (C) 2019 Tulir Asokan
|
# Copyright (C) 2021 Tulir Asokan
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
@ -16,12 +16,14 @@
|
|||||||
from typing import Optional, Union, IO
|
from typing import Optional, Union, IO
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import asyncio
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ruamel.yaml import YAML, YAMLError
|
from ruamel.yaml import YAML, YAMLError
|
||||||
from colorama import Fore
|
from aiohttp import ClientSession
|
||||||
from questionary import prompt
|
from questionary import prompt
|
||||||
|
from colorama import Fore
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from mautrix.types import SerializerError
|
from mautrix.types import SerializerError
|
||||||
@ -30,6 +32,7 @@ from ...loader import PluginMeta
|
|||||||
from ..cliq.validators import PathValidator
|
from ..cliq.validators import PathValidator
|
||||||
from ..base import app
|
from ..base import app
|
||||||
from ..config import get_token
|
from ..config import get_token
|
||||||
|
from ..cliq import cliq
|
||||||
from .upload import upload_file
|
from .upload import upload_file
|
||||||
|
|
||||||
yaml = YAML()
|
yaml = YAML()
|
||||||
@ -100,15 +103,16 @@ def write_plugin(meta: PluginMeta, output: Union[str, IO]) -> None:
|
|||||||
zip.write(file)
|
zip.write(file)
|
||||||
|
|
||||||
|
|
||||||
def upload_plugin(output: Union[str, IO], server: str) -> None:
|
@cliq.with_authenticated_http
|
||||||
|
async def upload_plugin(output: Union[str, IO], *, server: str, sess: ClientSession) -> None:
|
||||||
server, token = get_token(server)
|
server, token = get_token(server)
|
||||||
if not token:
|
if not token:
|
||||||
return
|
return
|
||||||
if isinstance(output, str):
|
if isinstance(output, str):
|
||||||
with open(output, "rb") as file:
|
with open(output, "rb") as file:
|
||||||
upload_file(file, server, token)
|
await upload_file(sess, file, server)
|
||||||
else:
|
else:
|
||||||
upload_file(output, server, token)
|
await upload_file(sess, output, server)
|
||||||
|
|
||||||
|
|
||||||
@app.command(short_help="Build a maubot plugin",
|
@app.command(short_help="Build a maubot plugin",
|
||||||
@ -137,4 +141,4 @@ def build(path: str, output: str, upload: bool, server: str) -> None:
|
|||||||
else:
|
else:
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
if upload:
|
if upload:
|
||||||
upload_plugin(output, server)
|
asyncio.run(upload_plugin(output, server=server))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# maubot - A plugin-based Matrix bot system.
|
# maubot - A plugin-based Matrix bot system.
|
||||||
# Copyright (C) 2019 Tulir Asokan
|
# Copyright (C) 2021 Tulir Asokan
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -13,12 +13,12 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from urllib.request import urlopen
|
|
||||||
from urllib.error import HTTPError
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from colorama import Fore
|
from colorama import Fore
|
||||||
|
from yarl import URL
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
from ..config import save_config, config
|
from ..config import save_config, config
|
||||||
from ..cliq import cliq
|
from ..cliq import cliq
|
||||||
@ -29,16 +29,17 @@ from ..cliq import cliq
|
|||||||
@cliq.option("-p", "--password", help="The password to your account", inq_type="password", required=True)
|
@cliq.option("-p", "--password", help="The password to your account", inq_type="password", required=True)
|
||||||
@cliq.option("-s", "--server", help="The server to log in to", default="http://localhost:29316", required=True)
|
@cliq.option("-s", "--server", help="The server to log in to", default="http://localhost:29316", required=True)
|
||||||
@cliq.option("-a", "--alias", help="Alias to reference the server without typing the full URL", default="", required=False)
|
@cliq.option("-a", "--alias", help="Alias to reference the server without typing the full URL", default="", required=False)
|
||||||
def login(server, username, password, alias) -> None:
|
@cliq.with_http
|
||||||
|
async def login(server: str, username: str, password: str, alias: str, sess: aiohttp.ClientSession) -> None:
|
||||||
data = {
|
data = {
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
}
|
}
|
||||||
try:
|
url = URL(server) / "_matrix/maubot/v1/auth/login"
|
||||||
with urlopen(f"{server}/_matrix/maubot/v1/auth/login",
|
async with sess.post(url, json=data) as resp:
|
||||||
data=json.dumps(data).encode("utf-8")) as resp_data:
|
if resp.status == 200:
|
||||||
resp = json.load(resp_data)
|
data = await resp.json()
|
||||||
config["servers"][server] = resp["token"]
|
config["servers"][server] = data["token"]
|
||||||
if not config["default_server"]:
|
if not config["default_server"]:
|
||||||
print(Fore.CYAN, "Setting", server, "as the default server")
|
print(Fore.CYAN, "Setting", server, "as the default server")
|
||||||
config["default_server"] = server
|
config["default_server"] = server
|
||||||
@ -46,9 +47,9 @@ def login(server, username, password, alias) -> None:
|
|||||||
config["aliases"][alias] = server
|
config["aliases"][alias] = server
|
||||||
save_config()
|
save_config()
|
||||||
print(Fore.GREEN + "Logged in successfully")
|
print(Fore.GREEN + "Logged in successfully")
|
||||||
except HTTPError as e:
|
else:
|
||||||
try:
|
try:
|
||||||
err = json.load(e)
|
err = (await resp.json())["error"]
|
||||||
except json.JSONDecodeError:
|
except (json.JSONDecodeError, KeyError):
|
||||||
err = {}
|
err = await resp.text()
|
||||||
print(Fore.RED + err.get("error", str(e)) + Fore.RESET)
|
print(Fore.RED + err + Fore.RESET)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# maubot - A plugin-based Matrix bot system.
|
# maubot - A plugin-based Matrix bot system.
|
||||||
# Copyright (C) 2019 Tulir Asokan
|
# Copyright (C) 2021 Tulir Asokan
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -13,45 +13,45 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
from urllib.request import urlopen, Request
|
|
||||||
from urllib.error import HTTPError
|
|
||||||
from typing import IO
|
from typing import IO
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from colorama import Fore
|
from colorama import Fore
|
||||||
|
from yarl import URL
|
||||||
|
import aiohttp
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from ..base import app
|
from ..cliq import cliq
|
||||||
from ..config import get_default_server, get_token
|
|
||||||
|
|
||||||
|
|
||||||
class UploadError(Exception):
|
class UploadError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.command(help="Upload a maubot plugin")
|
@cliq.command(help="Upload a maubot plugin")
|
||||||
@click.argument("path")
|
@click.argument("path")
|
||||||
@click.option("-s", "--server", help="The maubot instance to upload the plugin to")
|
@click.option("-s", "--server", help="The maubot instance to upload the plugin to")
|
||||||
def upload(path: str, server: str) -> None:
|
@cliq.with_authenticated_http
|
||||||
server, token = get_token(server)
|
async def upload(path: str, server: str, sess: aiohttp.ClientSession) -> None:
|
||||||
if not token:
|
print("hmm")
|
||||||
return
|
|
||||||
with open(path, "rb") as file:
|
with open(path, "rb") as file:
|
||||||
upload_file(file, server, token)
|
await upload_file(sess, file, server)
|
||||||
|
|
||||||
|
|
||||||
def upload_file(file: IO, server: str, token: str) -> None:
|
async def upload_file(sess: aiohttp.ClientSession, file: IO, server: str) -> None:
|
||||||
req = Request(f"{server}/_matrix/maubot/v1/plugins/upload?allow_override=true", data=file,
|
url = (URL(server) / "_matrix/maubot/v1/plugins/upload").with_query({"allow_override": "true"})
|
||||||
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/zip"})
|
headers = {"Content-Type": "application/zip"}
|
||||||
try:
|
async with sess.post(url, data=file, headers=headers) as resp:
|
||||||
with urlopen(req) as resp_data:
|
if resp.status == 200:
|
||||||
resp = json.load(resp_data)
|
data = await resp.json()
|
||||||
print(f"{Fore.GREEN}Plugin {Fore.CYAN}{resp['id']} v{resp['version']}{Fore.GREEN} "
|
print(f"{Fore.GREEN}Plugin {Fore.CYAN}{data['id']} v{data['version']}{Fore.GREEN} "
|
||||||
f"uploaded to {Fore.CYAN}{server}{Fore.GREEN} successfully.{Fore.RESET}")
|
f"uploaded to {Fore.CYAN}{server}{Fore.GREEN} successfully.{Fore.RESET}")
|
||||||
except HTTPError as e:
|
else:
|
||||||
try:
|
try:
|
||||||
err = json.load(e)
|
err = await resp.json()
|
||||||
except json.JSONDecodeError:
|
if "stacktrace" in err:
|
||||||
err = {}
|
print(err["stacktrace"])
|
||||||
print(err.get("stacktrace", ""))
|
err = err["error"]
|
||||||
print(Fore.RED + "Failed to upload plugin: " + err.get("error", str(e)) + Fore.RESET)
|
except (aiohttp.ContentTypeError, json.JSONDecodeError, KeyError):
|
||||||
|
err = await resp.text()
|
||||||
|
print(f"{Fore.RED}Failed to upload plugin: {err}{Fore.RESET}")
|
||||||
|
Loading…
Reference in New Issue
Block a user