diff --git a/README.md b/README.md index 20a0c571..5caa29e8 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ usage: matrixctl [-h] [--version] [-d] ... positional arguments: - {adduser,deluser,adduser-jitsi,deluser-jitsi,user,users,purge-history,rooms,delroom,update,upload,deploy,server-notice,start,restart,maintenance,check,version} + {adduser,deluser,adduser-jitsi,deluser-jitsi,user,users,purge-history,rooms,delroom,update,upload,deploy,server-notice,get-event,start,restart,maintenance,check,version} adduser Add a new matrix user deluser Deletes a user adduser-jitsi Add a new jitsi user @@ -39,6 +39,7 @@ positional arguments: upload Upload a file. deploy Provision and deploy server-notice Send a server notice + get-event get an event from the DB start Starts all OCI containers stop Stops all OCI containers restart Restarts all OCI containers (alias for start) diff --git a/docs/source/matrixctl.rst b/docs/source/matrixctl.rst index 10ae7903..84506386 100644 --- a/docs/source/matrixctl.rst +++ b/docs/source/matrixctl.rst @@ -143,6 +143,14 @@ server-notice :undoc-members: :show-inheritance: +get-event +--------- + +.. automodule:: matrixctl.get_event + :members: + :undoc-members: + :show-inheritance: + Helpers ======= diff --git a/matrixctl/__main__.py b/matrixctl/__main__.py index d9a0ee26..0b5274d0 100644 --- a/matrixctl/__main__.py +++ b/matrixctl/__main__.py @@ -35,6 +35,7 @@ from matrixctl.deluser import subparser_deluser from matrixctl.deluser_jitsi import subparser_deluser_jitsi from matrixctl.deploy import subparser_deploy +from matrixctl.get_event import subparser_get_event from matrixctl.maintenance import subparser_maintenance from matrixctl.purge_history import subparser_purge_history from matrixctl.rooms import subparser_rooms @@ -98,6 +99,7 @@ def setup_parser() -> argparse.ArgumentParser: subparser_upload, subparser_deploy, subparser_server_notice, + subparser_get_event, subparser_start, subparser_stop, subparser_restart, # alias for start @@ -130,7 +132,7 @@ def setup_logging(debug_mode: bool) -> None: # %(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s coloredlogs.install( - level="DEBUG" if debug_mode else "INFO", + level="DEBUG" if debug_mode else "WARNING", fmt=( "%(asctime)s %(name)s:%(lineno)d [%(funcName)s] %(levelname)s " "%(message)s" diff --git a/matrixctl/get_event.py b/matrixctl/get_event.py new file mode 100644 index 00000000..7df3798f --- /dev/null +++ b/matrixctl/get_event.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# matrixctl +# Copyright (c) 2020 Michael Sasser +# +# 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 . + +"""Use this module to get an event from the Database.""" + +from __future__ import annotations + +import json +import logging + +from argparse import ArgumentParser +from argparse import Namespace +from argparse import _SubParsersAction as SubParsersAction +from base64 import b64encode + +from .handlers.ssh import SSH +from .handlers.ssh import SSHResponse +from .handlers.toml import TOML +from .typing import JsonDict + + +__author__: str = "Michael Sasser" +__email__: str = "Michael@MichaelSasser.org" + + +logger = logging.getLogger(__name__) + +JID_EXT: str = "matrix-jitsi-web" + + +def subparser_get_event(subparsers: SubParsersAction) -> None: + """Create a subparser for the ``matrixctl get-event`` command. + + Parameters + ---------- + subparsers : argparse._SubParsersAction + The object which is returned by + ``parser.add_subparsers()``. + + Returns + ------- + None + + """ + parser: ArgumentParser = subparsers.add_parser( + "get-event", help="get an event from the DB" + ) + parser.add_argument("event_id", help="The event-id") + parser.set_defaults(func=get_event) + + +def get_event(arg: Namespace) -> int: + """Get an Event from the Server. + + It connects via paramiko to the server and runs the psql command provided + by the synapse playbook to run a query on the Database. + + Parameters + ---------- + arg : argparse.Namespace + The ``Namespace`` object of argparse's ``parse_args()`` + + Returns + ------- + err_code : int + Non-zero value indicates error code, or zero on success. + + """ + + toml: TOML = TOML() + address = ( + toml.get("SSH", "Address") + if toml.get("SSH", "Address") + else f"matrix.{toml.get('API', 'Domain')}" + ) + + # Workaround because of "$" through: paramiko - bash - psql + event64 = b64encode(arg.event_id.encode("utf-8")).decode("utf-8") + + query: str = "SELECT json FROM event_json WHERE event_id='$event'" + cmd: str = "/usr/local/bin/matrix-postgres-cli -P pager" + table: str = "synapse" + + command: str = ( + f"event=$(echo '{event64}' | base64 -d -) && " # Workaround + f'sudo {cmd} -d {table} -c "{query}"' + ) + + logger.debug(f"command: {command}") + + with SSH(address, toml.get("SSH", "User"), toml.get("SSH", "Port")) as ssh: + response: SSHResponse = ssh.run_cmd(command, tty=True) + + if not response.stderr: + logger.debug(f"response: {response.stdout}") + if response.stdout: + start: int = response.stdout.find("{") + stop: int = response.stdout.rfind("}") + 1 + + response_parsed: JsonDict = json.loads(response.stdout[start:stop]) + print(json.dumps(response_parsed, indent=4)) + return 0 + print("The response from the Database was empty.") + return 0 + logger.error(f"response: {response.stderr}") + print( + "An error occured during the query. Are you sure, you used the " + "correct event_id?" + ) + return 1 + + +# vim: set ft=python : diff --git a/matrixctl/handlers/ssh.py b/matrixctl/handlers/ssh.py index ac4c00da..0990bcec 100644 --- a/matrixctl/handlers/ssh.py +++ b/matrixctl/handlers/ssh.py @@ -111,13 +111,15 @@ def __str_from(f: ChannelFile) -> str | None: except OSError: return None - def run_cmd(self, cmd: str) -> SSHResponse: + def run_cmd(self, cmd: str, tty: bool = False) -> SSHResponse: """Run a command on the host machine and receive a response. Parameters ---------- cmd : str The command to run. + tty : bool + Request a pseudo-terminal from the server (default: ``False``) Returns ------- @@ -129,7 +131,10 @@ def run_cmd(self, cmd: str) -> SSHResponse: response: SSHResponse = SSHResponse( # skipcq: BAN-B601 - *[self.__str_from(s) for s in self.__client.exec_command(cmd)] + *[ + self.__str_from(s) + for s in self.__client.exec_command(cmd, get_pty=tty) + ] ) logger.debug(f'SSH Response: "{response}"') diff --git a/news/139.feature b/news/139.feature new file mode 100644 index 00000000..e1295a7b --- /dev/null +++ b/news/139.feature @@ -0,0 +1 @@ +Added the new command ``get-event``, which gets an event by ``event_id`` from the Database and prints it as JSON.