From 3dc7eab2ab7a4bd06a804ae9bb057acea5fe2917 Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 14 Sep 2026 16:56:20 +0200 Subject: [PATCH] fix(journal): ignore message edits so they stop recording duplicates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An edited `!journal ...` message was recorded as a brand-new entry, so one message edited five times produced five near-identical rows. Matrix delivers an edit as a fresh m.room.message, and mautrix swaps its content for `m.new_content` during deserialization (mautrix/types/event/message.py:393-396), stripping the "* " fallback prefix. The handler therefore saw something byte-identical to a new command. Neither @command.passive nor @command.new filters edits, so guard on evt.content.get_edit() in the plugin. Also corrects the README quirk that claimed edits could never reach the bot — it asserted the opposite of the actual behavior. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019wNB1sgkcBGijWjKTjtwG8 --- journal/README.md | 25 ++++++++++++++++++++----- journal/journal.py | 11 +++++++++++ journal/maubot.yaml | 2 +- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/journal/README.md b/journal/README.md index e7b84de..79b539c 100644 --- a/journal/README.md +++ b/journal/README.md @@ -58,11 +58,26 @@ 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. +- **Editing a recorded entry does nothing.** Edits are deliberately + ignored (since v0.2.1), so fixing a typo in a `!journal` message + leaves the stored entry unchanged. Send a fresh `!journal` message + instead of editing. + + This bullet previously claimed the opposite — that edits can't reach + the bot at all. That was wrong, and the bug it hid was noisy: mautrix + swaps an edit's content for `m.new_content` before the plugin sees it + (`mautrix/types/event/message.py:393-396`), stripping the `* ` + fallback prefix. The handler therefore received something identical + to a brand-new command and recorded a **duplicate entry on every + edit** — one journal message edited five times produced five rows. + Neither `@command.passive` nor `@command.new` filters edits, so any + maubot plugin matching on message bodies needs its own + `if evt.content.get_edit(): return` guard. + +- **Reactions are harmless.** They arrive as `m.reaction`, and + `@command.passive` only registers on `EventType.ROOM_MESSAGE` with + `msgtype` in `(m.text,)`, so a 👍 on a `!journal` message can never + trigger a recording. - **`!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`. diff --git a/journal/journal.py b/journal/journal.py index 2a78fe3..96dff19 100644 --- a/journal/journal.py +++ b/journal/journal.py @@ -58,6 +58,17 @@ class JournalBot(Plugin): @command.passive(regex=_JOURNAL_RE) async def journal(self, evt: MessageEvent, match) -> None: + # Ignore edits. An edit arrives as a *fresh* m.room.message whose + # content mautrix transparently replaces with `m.new_content` + # (mautrix/types/event/message.py:393-396), stripping the "* " + # fallback prefix. The result is byte-identical to a new command, + # so every edit of a `!journal ...` message would record another + # duplicate entry. The relation survives that swap, which is what + # get_edit() reads. Neither @command.passive nor @command.new + # filters edits, so plugins must do it themselves. + if evt.content.get_edit(): + return + rest = (match[1] or "").strip() if not rest: diff --git a/journal/maubot.yaml b/journal/maubot.yaml index 74e2ae3..15a8807 100644 --- a/journal/maubot.yaml +++ b/journal/maubot.yaml @@ -1,6 +1,6 @@ maubot: 0.1.0 id: dev.aiolabs.journal -version: 0.2.0 +version: 0.2.1 license: AGPL-3.0-or-later modules: - journal