Editing a `!journal` message now rewrites the entry it created rather
than recording another row. Entries are keyed on their source event id
(new `event_id` column, v2 migration); get_edit() yields that original
id, so the edit is matched back to its row.
`ts` keeps the original send time — it records when the work was logged,
not when the wording was fixed. Confirmation is a 📝 reaction on the
edited message, because replying to an edit event renders as "This event
could not be displayed" in Element and a reply per save is noise.
Supersedes
|
||
|---|---|---|
| .. | ||
| journal.py | ||
| maubot.yaml | ||
| README.md | ||
journal
Daily-journal Matrix bot. Each room member can record what they did, and anyone in the room can query the log.
Commands
!journal <what you did> 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):
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
event_id TEXT -- source event, so edits update in place (v2)
);
CREATE INDEX entries_user_ts ON entries (user, ts DESC);
CREATE INDEX entries_ts ON entries (ts DESC);
CREATE INDEX entries_event_id ON entries (event_id);
Wipe data via the maubot UI's per-instance Database tab:
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
-
Editing a
!journalmessage updates its entry (since v0.3.0). Fix a typo, add a line, and the stored entry changes in place — the bot confirms with a 📝 reaction on your message rather than posting a reply. The original send time is kept, sincetsrecords when the work was logged, not when the wording was corrected.This works because entries are keyed on the source event id, added in the v2 migration. Entries recorded before v0.3.0 have
event_id IS NULLand so can't be matched — editing one of those is a no-op.Worth knowing if you write other maubot plugins: mautrix swaps an edit's content for
m.new_contentbefore the handler sees it (mautrix/types/event/message.py:393-396), stripping the*fallback prefix, so an edit is indistinguishable from a new command. Neither@command.passivenor@command.newfilters them. A plugin that doesn't callevt.content.get_edit()will silently record a duplicate on every edit — which is exactly what this plugin did before v0.3.0. -
Reactions never trigger the bot.
@command.passiveregisters onEventType.ROOM_MESSAGEand filtersmsgtypeto(m.text,), so anm.reactionfails both checks. If a "Logged for" appears right after someone reacts, the trigger was an edit landing at the same moment — a reply to an edit event renders as "This event could not be displayed" in Element, which makes it look unrelated to any message. -
!journal show <random text>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
showortodayon 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<content> 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.