diff --git a/checks.py b/checks.py index 55eb4cf..7d1cadd 100644 --- a/checks.py +++ b/checks.py @@ -4,6 +4,7 @@ import time from urllib.parse import urlparse import aiohttp +from mcstatus import JavaServer TARGETS = [ { @@ -19,10 +20,11 @@ TARGETS = [ 'critical': True, }, { - 'name': 'Packs Site (Development)', - 'url': 'https://jakepc.rainbow-herring.ts.net/healthz/', - 'kind': 'packs', - 'critical': False, + 'name': 'Minecraft Server', + 'host': 'jakelaptop.rainbow-herring.ts.net', + 'port': 10000, + 'kind': 'minecraft', + 'critical': True, }, ] @@ -78,11 +80,12 @@ async def _resolve_public(session, resolver_url, host, timeout): return {'ok': True, 'ips': [a['data'] for a in answers], 'error': ''} -async def check_target(session, target, timeout): +async def check_minecraft(session, target, timeout): + host, port = target['host'], target['port'] result = { 'name': target['name'], - 'kind': target.get('kind', ''), - 'url': target['url'], + 'kind': 'minecraft', + 'url': f'{host}:{port}', 'critical': target.get('critical', True), 'up': False, 'status_code': None, @@ -91,39 +94,84 @@ async def check_target(session, target, timeout): 'version': None, 'detail': '', 'dns': {}, + 'players': None, + 'motd': None, } try: - status, latency_ms, body = await _get(session, target['url'], timeout) - result['status_code'] = status - result['latency_ms'] = latency_ms - - if status == 200: - data = {} - try: - data = json.loads(body) - except (json.JSONDecodeError, ValueError): - pass - result['up'] = data.get('status') == 'pass' - if data.get('db') is not None: - result['db'] = data['db'] - else: - result['detail'] = f'HTTP {status}' + server = JavaServer(host, port, timeout=timeout) + status = await server.async_status() except asyncio.TimeoutError: result['detail'] = 'timeout' - except aiohttp.ClientError: + return result + except OSError: result['detail'] = 'connection error' + return result except Exception as exc: result['detail'] = type(exc).__name__ + return result - if result['up'] and result['kind'] == 'gitea': + result['up'] = True + result['latency_ms'] = round(status.latency) + result['players'] = f"{status.players.online}/{status.players.max}" + if status.version and status.version.name: + result['version'] = status.version.name + try: + result['motd'] = status.motd.to_plain() + except Exception: + pass + return result + + +async def check_target(session, target, timeout): + if target.get('kind') == 'minecraft': + result = await check_minecraft(session, target, timeout) + else: + result = { + 'name': target['name'], + 'kind': target.get('kind', ''), + 'url': target['url'], + 'critical': target.get('critical', True), + 'up': False, + 'status_code': None, + 'latency_ms': None, + 'db': None, + 'version': None, + 'detail': '', + 'dns': {}, + } try: - status, _, body = await _get(session, GITEA_VERSION_URL, timeout) - if status == 200: - result['version'] = json.loads(body).get('version') - except Exception: - pass + status, latency_ms, body = await _get(session, target['url'], timeout) + result['status_code'] = status + result['latency_ms'] = latency_ms - host = urlparse(target['url']).hostname + if status == 200: + data = {} + try: + data = json.loads(body) + except (json.JSONDecodeError, ValueError): + pass + result['up'] = data.get('status') == 'pass' + if data.get('db') is not None: + result['db'] = data['db'] + else: + result['detail'] = f'HTTP {status}' + except asyncio.TimeoutError: + result['detail'] = 'timeout' + except aiohttp.ClientError: + result['detail'] = 'connection error' + except Exception as exc: + result['detail'] = type(exc).__name__ + + if result['up'] and result['kind'] == 'gitea': + try: + status, _, body = await _get(session, GITEA_VERSION_URL, timeout) + if status == 200: + result['version'] = json.loads(body).get('version') + except Exception: + pass + + url = target.get('url') or f'//{target["host"]}' + host = urlparse(url).hostname for label, resolver_url in DOH_RESOLVERS.items(): result['dns'][label] = await _resolve_public(session, resolver_url, host, timeout) diff --git a/main.py b/main.py index 67faf3d..c2547e3 100644 --- a/main.py +++ b/main.py @@ -70,13 +70,21 @@ class HealthBot(discord.Client): ) for r in results: emoji = '🟢' if r['up'] else '🔴' - code = r['status_code'] if r['status_code'] is not None else '—' latency = f"{r['latency_ms']} ms" if r['latency_ms'] is not None else '—' - hc = f'{emoji} `HTTP {code}` · {latency}' - if r.get('db') is not None: - hc += f' · db {r["db"]}' - if r.get('version'): - hc += f' · v{r["version"]}' + if r.get('kind') == 'minecraft': + players = r.get('players') or '—' + hc = f'{emoji} `{players} players` · {latency}' + if r.get('version'): + hc += f' · v{r["version"]}' + if r.get('motd'): + hc += f' · *{r["motd"]}*' + else: + code = r['status_code'] if r['status_code'] is not None else '—' + hc = f'{emoji} `HTTP {code}` · {latency}' + if r.get('db') is not None: + hc += f' · db {r["db"]}' + if r.get('version'): + hc += f' · v{r["version"]}' if r.get('detail'): hc += f' · *{r["detail"]}*' diff --git a/requirements.txt b/requirements.txt index fc0f8e1..c4d97b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ discord.py aiohttp python-dotenv +mcstatus==14.1.0