summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md82
-rw-r--r--README.md54
2 files changed, 134 insertions, 2 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 6ddfe28..b7a7ee5 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -63,8 +63,11 @@ src/
a2s.c/h Valve A2S_INFO UDP health probe with challenge handling.
clients/
- umbrella-cli/main.c CLI client. JSON over Unix socket. Interactive
- attach mode (epoll on socket + stdin).
+ umbrella-cli/main.c CLI client. JSON over Unix socket. Interactive
+ attach mode (epoll on socket + stdin).
+ umbrella-bot/umbrella-bot.py Matrix bot. Bridges a Matrix room to umbrella
+ units via the Unix socket. asyncio, matrix-nio
+ with E2E encryption. See "Matrix bot" section.
filters/
source.py Source engine filter (TF2, GMod): strips server_cvar,
@@ -130,6 +133,81 @@ Daemon → client responses:
{"type":"status", "name":"tf2", "state":"running", "players":4, "max":24, "map":"cp_badlands"}
```
+## Matrix bot
+
+### Dependencies
+
+```
+matrix-nio[e2e] aiofiles markdown
+```
+
+### Response formatting rules — never break these
+
+Matrix messages are sent with `format: org.matrix.custom.html`. The
+`body` field is plain text (client fallback). The `formatted_body`
+field must be valid HTML — not raw Markdown.
+
+Always convert via:
+```python
+md_lib.markdown(reply, extensions=["fenced_code", "nl2br"])
+```
+
+- `fenced_code` — renders triple-backtick code blocks as `<pre><code>`
+- `nl2br` — converts `\n` to `<br>` so multi-line responses don't
+ collapse into one paragraph
+
+Never use `reply.replace("\n", "<br>")` or pass raw Markdown to
+`formatted_body` — clients will display literal `**` and backticks.
+
+### Style rules for response strings
+
+- **No emojis** — use plain text (`Error:`, `Sent:`, etc.)
+- **No em dashes** — use a hyphen-minus (`-`)
+
+This applies to all strings the bot sends to Matrix: command responses,
+HELP_TEXT, error messages.
+
+### Power level
+
+Commands require power level >= 50 in the configured room. The check
+runs in `get_user_power_level()` before dispatching any command. Users
+below 50 get a plain error string and the command is not executed.
+
+### Commands and data flow
+
+| Command | Umbrella socket msg | Notes |
+|---------|--------------------|----|
+| `!units` | `{"cmd":"list"}` | Returns name, display, state, players, max_players per unit |
+| `!status <unit>` | `{"cmd":"status","unit":"..."}` | Returns state, players, max_players, map |
+| `!tail <unit>` | `{"cmd":"tail","unit":"..."}` | Returns full ring buffer (up to 500 lines); bot trims to last 30 |
+| `!cmd <unit> <cmd>` | `{"cmd":"input","unit":"...","data":"...\n"}` | Logged to `/var/log/umbrella/bot-audit.log` |
+| `!broadcast <msg>` | `{"cmd":"broadcast","message":"..."}` | Returns sent/failed counts |
+| `!restart <unit>` | `{"cmd":"action","unit":"...","action":"restart"}` | |
+| `!update <unit>` | `{"cmd":"action","unit":"...","action":"update"}` | |
+| `!action <unit> <action>` | `{"cmd":"action","unit":"...","action":"..."}` | |
+
+### Tail flow (bot side)
+
+```
+!tail tf2
+ -> umbrella.tail(unit) -> {"cmd":"tail","unit":"tf2"} over Unix socket
+ -> daemon responds {"type":"output","data":"line1\nline2\n...","history":true}
+ -> bot splits data into lines, trims to last TAIL_MAX_LINES (30)
+ -> wraps in triple-backtick block
+ -> sends with formatted_body via markdown conversion
+```
+
+### Config
+
+`/etc/umbrella/bot.conf` — homeserver, user_id, password, room_id,
+device_id, store_path, socket_path. `chmod 600` required (contains
+password). See `clients/umbrella-bot/bot.conf.example`.
+
+Encryption keys stored in SQLite at `store_path`
+(default `/etc/umbrella/bot-store`).
+
+First-run: `umbrella-bot.py --setup` to login and obtain device ID.
+
## YAML parser gotcha
`unit.c` uses libyaml's event API with a hand-rolled state machine.
diff --git a/README.md b/README.md
index e4c83a6..30b5db8 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,9 @@ multiple server processes via log file tailing, RCON, and A2S probing,
and exposes a Unix socket for CLI clients to attach, send commands, and
receive real-time output.
+Comes with two clients: `umbrella-cli` (terminal) and `umbrella-bot`
+(Matrix room bridge).
+
## Features
- Live log tailing via inotify with symlink resolution
@@ -108,6 +111,57 @@ umbrella-cli action <unit> <name> # run a named action script
umbrella-cli broadcast <message> # send to all units' broadcast_cmd
```
+## Matrix bot
+
+`umbrella-bot` bridges a Matrix room to umbrella units. Requires a
+dedicated Matrix account and a room the bot is invited to.
+
+### Dependencies
+
+```bash
+pip install matrix-nio[e2e] aiofiles markdown
+```
+
+### Setup
+
+```bash
+# 1. Copy and edit the config (chmod 600 — contains bot password)
+sudo cp clients/umbrella-bot/bot.conf.example /etc/umbrella/bot.conf
+sudo chmod 600 /etc/umbrella/bot.conf
+
+# 2. First-run login — prints the device ID to add to bot.conf
+python3 clients/umbrella-bot/umbrella-bot.py --setup
+
+# 3. Run normally (or via systemd)
+python3 clients/umbrella-bot/umbrella-bot.py
+```
+
+A systemd unit is provided at `clients/umbrella-bot/umbrella-bot.service`.
+
+### Bot commands
+
+Users require power level >= 50 in the room.
+
+| Command | Description |
+|---------|-------------|
+| `!units` | List all units with state and player counts |
+| `!status <unit>` | Show unit state, players, and map |
+| `!tail <unit>` | Dump the last 30 lines of buffered output |
+| `!cmd <unit> <command>` | Send a console command (logged to audit log) |
+| `!broadcast <message>` | Send message to all running units |
+| `!restart <unit>` | Run the restart action |
+| `!update <unit>` | Run the update action |
+| `!action <unit> <action>` | Run a named action |
+| `!help` | Show command list |
+
+### Config paths
+
+| Path | Purpose |
+|------|---------|
+| `/etc/umbrella/bot.conf` | Bot credentials and room ID |
+| `/etc/umbrella/bot-store/` | E2E encryption key store (SQLite) |
+| `/var/log/umbrella/bot-audit.log` | Audit log of all `!cmd` invocations |
+
## Reload
```bash