feat: direct chats need no mention; plain-text display name mentions #3

Merged
mxm merged 1 commit from feat/direct-chats into main 2026-10-14 18:14:42 +02:00
Owner

Summary

  • Direct chats: in a room with exactly two joined members (the bot and one other), every m.text message from the other member counts as addressed to the bot. Commands (!route, !route set fj <url>, !help, !name) work without a mention; other text is dispatched by the named-route rules (first word = route name, else default). A leading mention/pill is still stripped.
  • Detection: runner::room_contexts reads each joined room's joined members from the SDK state store at every sync (after the sync's state is applied); is_direct_chat = 2 members, one of them the bot. A room that gains a third member stops being direct from that sync on. m.direct/is_direct is not used.
  • Routes stay per room (no fallback), permissions unchanged (non-command users reach default only, change commands ignored). The bot's own messages never qualify (sender check), and unmentioned notices in a direct chat don't either, so bots cannot loop. Replies to reports work as before.
  • Display-name mentions: @<display name> as plain text (word-bounded, case-sensitive; @hl-n8nX does not match) counts as a mention and is stripped like the localpart. Uses the bot's room member display name, else the global name read at startup and updated after a successful !name.
  • README: Mentions and Direct chats sections; !help notes that direct chats need no mention.

Tests

cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all-features (112 passed). New tests cover: direct chat without mention (text + command), group room without mention, 3 members not direct, bot's own message/notice in a direct chat, unmentioned notices, non-command user to default, per-room routes without fallback, replies in a direct chat, display-name match/no match on prefix/case/stripping/rename.

Not tested: live against a homeserver (member reads from the SDK store are not unit-tested).

🤖 Generated with Claude Code

## Summary - **Direct chats:** in a room with exactly two joined members (the bot and one other), every `m.text` message from the other member counts as addressed to the bot. Commands (`!route`, `!route set fj <url>`, `!help`, `!name`) work without a mention; other text is dispatched by the named-route rules (first word = route name, else `default`). A leading mention/pill is still stripped. - Detection: `runner::room_contexts` reads each joined room's joined members from the SDK state store at every sync (after the sync's state is applied); `is_direct_chat` = 2 members, one of them the bot. A room that gains a third member stops being direct from that sync on. `m.direct`/`is_direct` is not used. - Routes stay per room (no fallback), permissions unchanged (non-command users reach `default` only, change commands ignored). The bot's own messages never qualify (sender check), and unmentioned notices in a direct chat don't either, so bots cannot loop. Replies to reports work as before. - **Display-name mentions:** `@<display name>` as plain text (word-bounded, case-sensitive; `@hl-n8nX` does not match) counts as a mention and is stripped like the localpart. Uses the bot's room member display name, else the global name read at startup and updated after a successful `!name`. - README: Mentions and Direct chats sections; `!help` notes that direct chats need no mention. ## Tests `cargo fmt --check`, `cargo clippy --all-targets --all-features -- -D warnings`, `cargo test --all-features` (112 passed). New tests cover: direct chat without mention (text + command), group room without mention, 3 members not direct, bot's own message/notice in a direct chat, unmentioned notices, non-command user to `default`, per-room routes without fallback, replies in a direct chat, display-name match/no match on prefix/case/stripping/rename. Not tested: live against a homeserver (member reads from the SDK store are not unit-tested). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
In a room with exactly two joined members, the bot and one other, every
text message from the other member counts as addressed to the bot:
commands work without a mention and other text is dispatched by the
named-route rules. Membership is read from the SDK state store at every
sync, so a room that gains a third member stops being direct. Routes stay
per room and permissions are unchanged; the bot's own messages and
unmentioned notices never qualify.

The bot's display name written as "@<name>" (word-bounded,
case-sensitive) also counts as a mention and is stripped from the text.
The room member display name is preferred; the global name is read at
startup and updated after !name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mxm merged commit 8d5fbd9a55 into main 2026-10-14 18:14:42 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mxm/matrix-relay-bot!3
No description provided.