import argparse import logging import re import subprocess import threading from typing import List import message_queue LOG = logging.getLogger(__name__) class ProcessWrapper: """Iterator that spawns a process and yields lines from its stdout.""" def __init__(self, command: List[str]): self.proc = subprocess.Popen( " ".join(command), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True, ) def __iter__(self): return iter(self.proc.stdout.readline, "") def send(self, msg): self.proc.stdin.write(msg) def wait(self): return self.proc.wait() def send_process_output( process: ProcessWrapper, msg_queue: message_queue.MessageQueue ): # "[07:36:28] [Server thread/INFO] [minecraft/DedicatedServer]: test" prog = re.compile(r"\[.*\] \[(.*)\] \[(.*)\]: <(.*)> (.*)") for line in process: LOG.info(line.rstrip("\n")) result = prog.search(line) if result: LOG.info( "user: {} msg: {}".format( result.group(3), result.group(4).rstrip("\n"), ) ) msg_queue.add( {"user": result.group(3), "msg": result.group(4).rstrip("\n")}, ) def relay_queue_input( process: ProcessWrapper, msg_queue: message_queue.MessageQueue ): for message in msg_queue: if "command" in message: process.send(message["command"]) else: LOG.debug(message) def main(): logging.basicConfig(level=logging.DEBUG) parser = argparse.ArgumentParser() parser.add_argument("--matrix_server", required=True) parser.add_argument("--matrix_server_port", type=int, default=5001) parser.add_argument("command", nargs=argparse.REMAINDER) args = parser.parse_args() LOG.info("Running Minecraft Server Wrapper") wrapper = ProcessWrapper(args.command) queue = message_queue.MessageQueue( host=args.matrix_server, port=args.matrix_server_port, side=message_queue.Side.CLIENT, ) send_worker = threading.Thread( target=send_process_output, args=(wrapper, queue), daemon=True, ) receive_worker = threading.Thread( target=relay_queue_input, args=(wrapper, queue), daemon=True, ) send_worker.start() receive_worker.start() LOG.info("All threads created") send_worker.join() receive_worker.join() queue.close() LOG.info("All threads terminated") return wrapper.wait() if __name__ == "__main__": main()