diff options
| author | auric <auric@japegames.com> | 2026-02-22 21:26:48 -0600 |
|---|---|---|
| committer | auric <auric@japegames.com> | 2026-02-22 21:26:48 -0600 |
| commit | f1af8621c19390b81847eeccd4e47b1ddb42d4ab (patch) | |
| tree | a121dbb04c4653e3cd928bda8f623af026afa038 /clients/umbrella-bot/umbrella-bot.py | |
| parent | e20456da3d77fa15c2e6b93584142b2808e3b6a4 (diff) | |
umbrella-bot: fix Matrix markdown rendering, remove emojis and em dashes
formatted_body was passing raw Markdown to Matrix clients, causing literal
asterisks and backticks to display instead of rendered formatting. Now
converts to HTML via the markdown library with the fenced_code extension.
Also replaced emoji characters (checkmarks, X marks) with plain text and
substituted em dashes with hyphens throughout response strings and HELP_TEXT.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'clients/umbrella-bot/umbrella-bot.py')
| -rwxr-xr-x | clients/umbrella-bot/umbrella-bot.py | 55 |
1 files changed, 28 insertions, 27 deletions
diff --git a/clients/umbrella-bot/umbrella-bot.py b/clients/umbrella-bot/umbrella-bot.py index 6e26947..d6c6edb 100755 --- a/clients/umbrella-bot/umbrella-bot.py +++ b/clients/umbrella-bot/umbrella-bot.py @@ -6,7 +6,7 @@ Connects to the umbrella Unix socket and bridges commands from a Matrix room to umbrella units. Requires matrix-nio with encryption support. Dependencies: - pip install matrix-nio[e2e] aiofiles + pip install matrix-nio[e2e] aiofiles markdown Setup: 1. Create /etc/umbrella/bot.conf (see bot.conf.example) @@ -16,6 +16,7 @@ Setup: import asyncio import json +import markdown as md_lib import os import re import signal @@ -62,15 +63,15 @@ TAIL_MAX_LINES = 30 HELP_TEXT = """\ Umbrella bot commands: - !status <unit> — Show unit status - !cmd <unit> <command> — Send a console command - !tail <unit> — Show recent output from a unit - !restart <unit> — Restart the unit's systemd service - !update <unit> — Run the update action - !action <unit> <action> — Run a named action - !broadcast <message> — Send message to all running units - !units — List all units - !help — Show this message + !status <unit> - Show unit status + !cmd <unit> <command> - Send a console command + !tail <unit> - Show recent output from a unit + !restart <unit> - Restart the unit's systemd service + !update <unit> - Run the update action + !action <unit> <action> - Run a named action + !broadcast <message> - Send message to all running units + !units - List all units + !help - Show this message """ # ── Umbrella socket client ──────────────────────────────────────────────────── @@ -209,7 +210,7 @@ async def handle_command( # Check power level power = await get_user_power_level(client, room.room_id, sender) if power < 50: - return f"❌ Permission denied. Requires power level ≥ 50 (you have {power})." + return f"Permission denied. Requires power level >= 50 (you have {power})." try: if cmd == "!help": @@ -224,7 +225,7 @@ async def handle_command( pl = u.get("players", -1) mx = u.get("max_players", -1) players = f" ({pl}/{mx})" if pl >= 0 and mx >= 0 else "" - lines.append(f" `{u['name']}` — {u['display']} [{u['state']}{players}]") + lines.append(f" `{u['name']}` - {u['display']} [{u['state']}{players}]") return "\n".join(lines) elif cmd == "!status": @@ -233,7 +234,7 @@ async def handle_command( unit = parts[1] resp = umbrella.status(unit) if resp.get("type") == "error": - return f"❌ {resp['message']}" + return f"Error: {resp['message']}" pl = resp.get("players", -1) mx = resp.get("max_players", -1) map_name = resp.get("map", "") @@ -254,9 +255,9 @@ async def handle_command( command = " ".join(parts[2:]) resp = umbrella.send_input(unit, command) if resp.get("type") == "error": - return f"❌ {resp['message']}" + return f"Error: {resp['message']}" audit_log(sender, unit, command) - return f"✓ Sent: `{command}`" + return f"Sent: `{command}`" elif cmd == "!tail": if len(parts) < 2: @@ -264,7 +265,7 @@ async def handle_command( unit = parts[1] resp = umbrella.tail(unit) if resp.get("type") == "error": - return f"❌ {resp.get('message', 'unknown error')}" + return f"Error: {resp.get('message', 'unknown error')}" data = resp.get("data", "") if not data or not data.strip(): return f"No output buffered for `{unit}`." @@ -284,10 +285,10 @@ async def handle_command( message = " ".join(parts[1:]) resp = umbrella.broadcast(message) if resp.get("type") == "error": - return f"❌ {resp.get('message', 'unknown error')}" + return f"Error: {resp.get('message', 'unknown error')}" sent = resp.get("sent", 0) failed = resp.get("failed", 0) - result = f"✓ Broadcast sent to {sent} unit(s)" + result = f"Broadcast sent to {sent} unit(s)" if failed: result += f", {failed} failed" return result @@ -298,8 +299,8 @@ async def handle_command( unit = parts[1] resp = umbrella.run_action(unit, "restart") if resp.get("type") == "error": - return f"❌ {resp['message']}" - return f"✓ Restart dispatched for `{unit}`" + return f"Error: {resp['message']}" + return f"Restart dispatched for `{unit}`" elif cmd == "!update": if len(parts) < 2: @@ -307,8 +308,8 @@ async def handle_command( unit = parts[1] resp = umbrella.run_action(unit, "update") if resp.get("type") == "error": - return f"❌ {resp['message']}" - return f"✓ Update dispatched for `{unit}`" + return f"Error: {resp['message']}" + return f"Update dispatched for `{unit}`" elif cmd == "!action": if len(parts) < 3: @@ -317,14 +318,14 @@ async def handle_command( action = parts[2] resp = umbrella.run_action(unit, action) if resp.get("type") == "error": - return f"❌ {resp['message']}" - return f"✓ Action `{action}` dispatched for `{unit}`" + return f"Error: {resp['message']}" + return f"Action `{action}` dispatched for `{unit}`" except RuntimeError as e: - return f"❌ Umbrella error: {e}" + return f"Umbrella error: {e}" except Exception as e: log.exception(f"Command error: {e}") - return f"❌ Internal error: {e}" + return f"Internal error: {e}" return None @@ -398,7 +399,7 @@ async def run_bot(config: dict): "msgtype": "m.text", "body": reply, "format": "org.matrix.custom.html", - "formatted_body": reply.replace("\n", "<br>"), + "formatted_body": md_lib.markdown(reply, extensions=["fenced_code"]), }, ignore_unverified_devices=True, ) |
