From b21ad2890fc3ecd6351aa7eb4e65ee08e9e68a59 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 24 May 2026 09:55:24 +0200 Subject: [PATCH] docs: add umbrella + journal READMEs Root README orients new contributors on the build/upload/iterate loop and points at ~/dev/CLAUDE.md for maubot patterns. journal/ README covers the three commands, the SQLite schema, known quirks (edits don't re-trigger, subcommand detection scope), and documents why this plugin uses @command.passive instead of the more obvious @command.new. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 58 +++++++++++++++++++++++++++++++++ journal/README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 README.md create mode 100644 journal/README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b7c973 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# maubot-plugins + +Umbrella for [maubot](https://github.com/maubot/maubot) plugins used by +the aiolabs / Château du Faune Matrix stack. The maubot daemon itself +is provisioned via `server-deploy/modules/services/maubot.nix` on the +castle hosts; the actual plugin code lives here. + +## Plugins + +| Plugin | Purpose | +|---|---| +| [`journal/`](./journal/) | Farm-journal bot. `!journal ` records what you did, scoped per-user/room/timestamp. `!journal show [@user]` and `!journal today` query back. | + +## Building a plugin + +A `.mbp` is just a zip containing `maubot.yaml` + the plugin's Python +modules at the root. No special tooling needed: + +```sh +cd / +zip -j ../.mbp maubot.yaml *.py +``` + +(`-j` strips the directory prefix so files land at the zip root.) + +## Uploading / iterating + +1. Open the maubot UI (e.g. `https://maubot./_matrix/maubot/`). +2. **Plugins → +** (first time) or click the existing plugin → upload + the new `.mbp`. Maubot keys plugins by `id`; uploading a new + `version` of the same `id` replaces the old one. +3. **Hit Save** on the affected instance after upload — toggling + Enabled without Save will revert. Easy facepalm. + +Bump `version:` in `maubot.yaml` for every meaningful change so the +maubot UI surfaces it cleanly and old `.mbp` files in +`/var/lib/maubot/plugins/` aren't ambiguous. + +## Bot account convention + +Each plugin attaches to a Matrix client (a regular Matrix user account +controlled by maubot). For the journal bot: `@journalbot:ariege.io`. +Bot accounts are created the same way as any user — issue a +registration token from the Continuwuity admin room +(`!admin token issue --once`) and register through Element, then add +the client in the maubot UI. + +Invite the bot to whichever rooms it should serve via `/invite +@:` — maubot's autojoin handles new invites that arrive +after the client's sync loop is up. + +## Patterns + gotchas + +Maubot-specific patterns (command decorators, multi-line caveats, +`database_type` in `maubot.yaml`, etc.) live in `~/dev/CLAUDE.md` +under "Maubot plugin development". Read that before writing a new +plugin — there are several footguns that look fine but silently lose +data. diff --git a/journal/README.md b/journal/README.md new file mode 100644 index 0000000..e7b84de --- /dev/null +++ b/journal/README.md @@ -0,0 +1,82 @@ +# journal + +Daily-journal Matrix bot. Each room member can record what they did, +and anyone in the room can query the log. + +## Commands + +``` +!journal record an entry (multi-line OK) +!journal show [@user:domain] last 10 entries, optionally filtered by user +!journal today all entries from today (UTC) +``` + +Multi-line works either inline or after a newline: + +``` +!journal Did three things today: +- planted garlic +- mucked out the goat pen +- finished the irrigation patch +``` + +``` +!journal +- planted garlic +- mucked out the goat pen +``` + +Both record the full body verbatim. + +## Storage + +One SQLite database per maubot instance, at +`/var/lib/maubot/plugin-dbs/journal.db` on the host. Schema (managed +by `mautrix.util.async_db.UpgradeTable`): + +```sql +CREATE TABLE entries ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user TEXT NOT NULL, -- @sender:domain + room TEXT NOT NULL, -- !roomid:domain + ts BIGINT NOT NULL, -- ms since epoch (from evt.timestamp) + text TEXT NOT NULL -- raw entry body +); +CREATE INDEX entries_user_ts ON entries (user, ts DESC); +CREATE INDEX entries_ts ON entries (ts DESC); +``` + +Wipe data via the maubot UI's per-instance **Database** tab: + +```sql +DELETE FROM entries; +DELETE FROM sqlite_sequence WHERE name = 'entries'; +``` + +(The second line resets the auto-increment counter; skip it if you'd +rather keep IDs monotonic across resets.) + +## Known quirks + +- **Edited messages don't re-trigger the bot.** Matrix sends edits as + a separate `m.replace` event that bots don't react to. If you typed + `!journal` then edited the message to add content, the bot saw only + the empty `!journal` and won't record. Send a fresh message instead + of editing. +- **`!journal show ` runs the show query with that text + as the user filter.** If it doesn't match any MXID, you get + "No entries." Use a fully-qualified MXID like `@pat:ariege.io`. +- **Subcommand detection only looks at the first line.** Anything + starting with `show ` or `today` on the first line dispatches to + the query handlers; anything else (including prose that happens to + contain "show" mid-text) records as an entry. + +## Architecture note + +This plugin uses `@command.passive` with a regex matcher rather than +`@command.new`. The reason — and why other plugins should consider the +same pattern for prose-input commands — is documented in `~/dev/CLAUDE.md` +under "Multi-line freeform parent commands". Short version: +`@command.new` silently drops `!journal\n` because maubot's +parser only treats space as the command/args delimiter, leading to +invisible data loss when users naturally hit Enter after the command.