bot/libminecraft.py
2025-12-16 01:15:17 +01:00

106 lines
3.6 KiB
Python

from re import compile
from typing import Any, Callable
from aiohttp import ClientSession
class Minecraft:
def __init__(self, host: str, port: int, password: str):
self.host = host.rstrip("/")
self.port = port
self.headers = {"Authorization": password}
self._hooks: list[Callable] = []
async def _get(self, method: str, **json: Any):
async with ClientSession() as session:
async with session.get(
f"{self.host}:{self.port}{method}", json=json, headers=self.headers
) as response:
if response.status == 200:
return await response.json()
raise ConnectionError(
f"Error while sending {method} to Server: HTTP code {response.status}."
)
async def _stream(self, method: str, **json: Any):
async with ClientSession() as session:
async with session.get(
f"{self.host}:{self.port}{method}", json=json, headers=self.headers
) as response:
if response.status == 200:
async for chunk, _ in response.content.iter_chunks():
yield chunk
else:
raise ConnectionError(
f"Error while sending {method} to Server: HTTP code {response.status}."
)
async def start(self):
return await self._get("/start")
async def status(self):
return await self._get("/status")
async def stop(self, countdown: int = 60, reason: str = "", timeout: int = 10):
return await self._get(
"/stop", countdown=countdown, reason=reason, timeout=timeout
)
async def restart(self, countdown: int = 60, reason: str = "", timeout: int = 10):
return await self._get(
"/restart", countdown=countdown, reason=reason, timeout=timeout
)
async def maintainance(
self, countdown: int = 60, reason: str = "", timeout: int = 10
):
return await self._get(
"/maintainance", countdown=countdown, reason=reason, timeout=timeout
)
async def command(self, command: str):
return await self._get("/command", command=command)
async def logs_stream(self):
async for chunk in self._stream("/logs/stream"):
yield chunk.decode().strip()
async def logs_tail(self, back: int = 10):
async for chunk in self._stream("/logs/tail", back=back):
yield chunk.decode().strip()
def onConsoleLog(self, pattern: str | None = None) -> Callable:
def wrapper(func: Callable):
compiled = compile(pattern) if pattern else None
async def callback(line: str):
if compiled is None:
func(line)
return
match = compiled.fullmatch(line)
if match is None:
return
groups = match.groups()
if groups:
await func(*groups)
else:
await func(line)
self._hooks.append(callback)
return func
return wrapper
def onPlayerChat(self) -> Callable:
def wrapper(func: Callable):
return self.onConsoleLog(
r"\[[0-9]{2}:[0-9]{2}:[0-9]{2}\] \[Server thread\/INFO\]: <(.*)> (.*)"
)(func)
return wrapper
async def loop(self):
async for line in self.logs_stream():
for callback in self._hooks:
await callback(line)