Summary
In rooms with more than two members, opencrow should only respond when it is
explicitly mentioned (e.g. @opencrow_one). In 1:1 rooms (just the bot and one
other user) the behaviour stays exactly as today: respond to every allowed
message.
Motivation
The Matrix backend currently answers every message from an allowed sender,
regardless of room size and with no addressing. This is fine for a 1:1 bot but
breaks down in shared rooms:
- Multiple opencrow instances in the same room all reply to every message — N
answers per prompt and N concurrent LLM calls.
- Even a single bot in a human group chat is noisy: it replies to messages that
were not meant for it.
Requiring an explicit mention in group rooms makes bots addressable and quiet by
default, while keeping the frictionless 1:1 experience.
Current behaviour
matrix/backend.go filterMessage drops a message only when: not initial-synced,
the sender is the bot itself, the sender is not in the allowlist, or the msgtype
isn't text/media. There is no room-size or mention check — every remaining message
reaches b.handler and gets a reply.
Desired behaviour
- 1:1 room (exactly 2 joined members: the bot + one other): unchanged —
respond to all allowed messages.
- Group room (>2 joined members): respond only if the message explicitly
addresses this bot.
"Addresses this bot" should mean any of:
- An intentional mention:
m.mentions.user_ids contains the bot's own MXID — the
robust, spec-compliant signal (mautrix exposes this as
event.MessageEventContent.Mentions).
- (Optional) a reply (
m.in_reply_to) to one of the bot's own messages.
- (Optional fallback)
body / formatted_body contains the bot's localpart or a
matrix.to pill, for clients that don't emit m.mentions.
When the bot does respond, the mention token should be stripped from the text sent
to the model so the prompt isn't polluted with @opencrow_one.
Implementation sketch
- Add the gate in
filterMessage (or right after, where the room is known):
- Determine the joined-member count for
evt.RoomID (e.g. via
Client.JoinedMembers, cached / derived from sync state rather than a request
per message).
- members <= 2 → keep current behaviour.
- members > 2 → require the bot's MXID (
b.userID) in the message's
m.mentions.user_ids, otherwise drop.
- Consider a config flag (e.g.
OPENCROW_MATRIX_REQUIRE_MENTION_IN_GROUPS,
default true) so it can be disabled.
Edge cases / notes
- "1:1" = exactly 2 joined members; ignore invited/left state.
- A room with one human + two bots counts as a group, so each bot replies only
when its own MXID is mentioned — this is the intended multi-bot UX.
- Encrypted rooms: mention detection runs on decrypted content, so it works the
same way.
- Member count must track join/leave (membership events already flow through the
syncer).
- Prefer
m.mentions over display-name matching (the latter is ambiguous).
Acceptance criteria
- In a 2-member room, the bot replies to allowed messages with no mention
(unchanged).
- In a >2-member room, the bot replies only to allowed messages that mention its
MXID; unaddressed messages are ignored.
- With multiple bots in one room, mentioning
@opencrow_one triggers only
opencrow_one.
- The mention token is not included in the text sent to the model.
- (If added) the config flag toggles the group-room requirement.
Summary
In rooms with more than two members, opencrow should only respond when it is
explicitly mentioned (e.g.
@opencrow_one). In 1:1 rooms (just the bot and oneother user) the behaviour stays exactly as today: respond to every allowed
message.
Motivation
The Matrix backend currently answers every message from an allowed sender,
regardless of room size and with no addressing. This is fine for a 1:1 bot but
breaks down in shared rooms:
answers per prompt and N concurrent LLM calls.
were not meant for it.
Requiring an explicit mention in group rooms makes bots addressable and quiet by
default, while keeping the frictionless 1:1 experience.
Current behaviour
matrix/backend.gofilterMessagedrops a message only when: not initial-synced,the sender is the bot itself, the sender is not in the allowlist, or the msgtype
isn't text/media. There is no room-size or mention check — every remaining message
reaches
b.handlerand gets a reply.Desired behaviour
respond to all allowed messages.
addresses this bot.
"Addresses this bot" should mean any of:
m.mentions.user_idscontains the bot's own MXID — therobust, spec-compliant signal (mautrix exposes this as
event.MessageEventContent.Mentions).m.in_reply_to) to one of the bot's own messages.body/formatted_bodycontains the bot's localpart or amatrix.to pill, for clients that don't emit
m.mentions.When the bot does respond, the mention token should be stripped from the text sent
to the model so the prompt isn't polluted with
@opencrow_one.Implementation sketch
filterMessage(or right after, where the room is known):evt.RoomID(e.g. viaClient.JoinedMembers, cached / derived from sync state rather than a requestper message).
b.userID) in the message'sm.mentions.user_ids, otherwise drop.OPENCROW_MATRIX_REQUIRE_MENTION_IN_GROUPS,default
true) so it can be disabled.Edge cases / notes
when its own MXID is mentioned — this is the intended multi-bot UX.
same way.
syncer).
m.mentionsover display-name matching (the latter is ambiguous).Acceptance criteria
(unchanged).
MXID; unaddressed messages are ignored.
@opencrow_onetriggers onlyopencrow_one.