feat: native matrix URI pills for {user}/{room} + major rendering & codebase refactor

This change introduces native `matrix:` URI-based rendering for `{user}` and `{room}` placeholders,
replacing previous plaintext and matrix.to-based links. Users and rooms are now rendered as clickable
pills in supporting clients, with a clean display using display names and room names (no @/# prefixes).

Reporting, moderation, and auto-redaction messages have been updated to use the same rendering logic.
Inspect and event links now also use native `matrix:` URIs for direct in-client navigation.

Internally, URI generation and rendering logic have been unified via central helper functions,
ensuring consistent handling of user IDs, room IDs, aliases, and event IDs.

This commit also includes a broader refactor of the codebase:
- decomposed complex flows (e.g. join handling) into smaller helpers
- moved mutable class-level state to instance-level
- reduced duplicate API calls and redundant logic
- improved overall structure and maintainability

Test coverage has been extended for URI helpers and rendering logic to prevent regressions.

No breaking changes to existing template parameters like `{user_link}` or `{room_link}`.
This commit is contained in:
2026-04-11 20:21:33 +02:00
parent 933865d80c
commit edd3eee178
40 changed files with 474 additions and 382 deletions
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
+56
View File
@@ -0,0 +1,56 @@
import html
from types import SimpleNamespace
import pytest
from community.bot import CommunityBot, DEFAULT_ROOM_PILL_PREFIX, DEFAULT_USER_PILL_PREFIX
@pytest.fixture()
def bot() -> CommunityBot:
plugin = CommunityBot.__new__(CommunityBot)
plugin.config = {}
return plugin
def test_user_uri_helper_strips_at_and_uses_chat_action(bot: CommunityBot) -> None:
assert bot._matrix_user_uri("@alice:example.org") == "matrix:u/alice:example.org?action=chat"
def test_room_uri_helper_prefers_alias(bot: CommunityBot) -> None:
assert bot._matrix_room_uri("!roomid:example.org", "#general:example.org") == "matrix:r/general:example.org"
def test_room_uri_helper_falls_back_to_room_id_without_bang(bot: CommunityBot) -> None:
assert bot._matrix_room_uri("!roomid:example.org", None) == "matrix:roomid/roomid:example.org"
def test_event_uri_helper_strips_prefixes(bot: CommunityBot) -> None:
assert bot._matrix_event_uri("!roomid:example.org", "$eventid") == "matrix:roomid/roomid:example.org/e/eventid"
def test_format_user_pill_uses_clean_default_prefix(bot: CommunityBot) -> None:
plain, formatted = bot._format_user_pill("@alice:example.org", "Alice")
assert plain == f"{DEFAULT_USER_PILL_PREFIX}Alice"
assert 'href="matrix:u/alice:example.org?action=chat"' in formatted
assert ">Alice<" in formatted
def test_format_room_pill_uses_alias_when_available(bot: CommunityBot) -> None:
plain, formatted = bot._format_room_pill("!roomid:example.org", "General", "#general:example.org")
assert plain == f"{DEFAULT_ROOM_PILL_PREFIX}General"
assert formatted == '<a href="matrix:r/general:example.org">General</a>'
def test_format_room_pill_falls_back_to_room_id(bot: CommunityBot) -> None:
plain, formatted = bot._format_room_pill("!roomid:example.org", "General", None)
assert plain == f"{DEFAULT_ROOM_PILL_PREFIX}General"
assert formatted == '<a href="matrix:roomid/roomid:example.org">General</a>'
def test_format_user_pill_escapes_displayname(bot: CommunityBot) -> None:
plain, formatted = bot._format_user_pill("@alice:example.org", '<Admin & Ops>')
assert plain == f"{DEFAULT_USER_PILL_PREFIX}<Admin & Ops>"
# Keep this broad enough to avoid coupling to quote style.
assert "matrix:u/alice:example.org?action=chat" in formatted
assert html.escape('<Admin & Ops>') in formatted
Executable → Regular
View File
+10
View File
@@ -0,0 +1,10 @@
from community.bot import CommunityBot
def test_matrix_uri_wrappers_delegate_to_canonical_helpers() -> None:
bot = CommunityBot.__new__(CommunityBot)
bot.config = {}
assert bot._matrix_user_uri("@alice:example.org") == "matrix:u/alice:example.org?action=chat"
assert bot._matrix_room_uri("!roomid:example.org") == "matrix:roomid/roomid:example.org"
assert bot._matrix_room_uri("!roomid:example.org", "#general:example.org") == "matrix:r/general:example.org"
assert bot._matrix_event_uri("!roomid:example.org", "$eventid") == "matrix:roomid/roomid:example.org/e/eventid"
Executable → Regular
View File
Executable → Regular
View File
View File
Executable → Regular
+50 -3
View File
@@ -12,7 +12,11 @@ from community.bot import CommunityBot
def bot():
bot = CommunityBot.__new__(CommunityBot)
bot.client = Mock()
bot.config = {"matrix_to_base_url": "https://matrix.to"}
bot.config = {
"matrix_to_base_url": "https://matrix.to",
"user_pill_prefix": "@",
"room_pill_prefix": "#",
}
return bot
@@ -51,7 +55,50 @@ def test_render_message_template_supports_user_id_and_user_link(bot):
"General",
)
assert plain == "Alice / @alice:example.org / https://matrix.to/#/@alice:example.org"
assert "Alice / @alice:example.org / " in html
assert plain == "@Alice / @alice:example.org / https://matrix.to/#/@alice:example.org"
assert "@Alice / @alice:example.org / " in html
assert '<a href=' in html
assert '>Alice</a>' in html
def test_render_message_template_uses_configurable_user_and_room_pill_prefixes(bot):
bot.config["user_pill_prefix"] = ""
bot.config["room_pill_prefix"] = ""
plain, html = bot._render_message_template(
"{user} has joined {room}.",
"@alice:example.org",
"Alice",
"!room:example.org",
"General",
)
assert plain == "Alice has joined General."
assert "<a href='matrix:u/alice:example.org?action=chat'>Alice</a>" in html
assert "<a href='matrix:roomid/room:example.org'>General</a>" in html
def test_render_message_template_defaults_to_prefixed_user_and_room_pills(bot):
plain, html = bot._render_message_template(
"{user} has joined {room}.",
"@alice:example.org",
"Alice",
"!room:example.org",
"General",
)
assert plain == "@Alice has joined #General."
assert "<a href='matrix:u/alice:example.org?action=chat'>@Alice</a>" in html
assert "<a href='matrix:roomid/room:example.org'>#General</a>" in html
def test_matrix_uri_helpers_are_consistent():
from community.bot import CommunityBot
bot = CommunityBot.__new__(CommunityBot)
bot.config = {}
assert bot._matrix_user_uri("@alice:example.org") == "matrix:u/alice:example.org?action=chat"
assert bot._matrix_room_uri("!roomid:example.org", "#general:example.org") == "matrix:r/general:example.org"
assert bot._matrix_room_uri("!roomid:example.org", None) == "matrix:roomid/roomid:example.org"
assert bot._matrix_event_uri("!roomid:example.org", "$eventid") == "matrix:roomid/roomid:example.org/e/eventid"
Executable → Regular
View File