feat(matrix): add group policy and strict mention gating

This commit is contained in:
Alexander Minges
2026-02-10 15:30:39 +01:00
parent 3200135f4b
commit fa2049fc60
2 changed files with 50 additions and 8 deletions

View File

@@ -266,18 +266,55 @@ class MatrixChannel(BaseChannel):
await self.client.join(room.room_id) await self.client.join(room.room_id)
def _is_direct_room(self, room: MatrixRoom) -> bool:
"""Return True if the room behaves like a DM (2 or fewer members)."""
member_count = getattr(room, "member_count", None)
return isinstance(member_count, int) and member_count <= 2
def _is_bot_mentioned_from_mx_mentions(self, event: RoomMessageText) -> bool:
"""Resolve mentions strictly from Matrix-native m.mentions payload."""
source = getattr(event, "source", None)
if not isinstance(source, dict):
return False
content = source.get("content")
if not isinstance(content, dict):
return False
mentions = content.get("m.mentions")
if not isinstance(mentions, dict):
return False
user_ids = mentions.get("user_ids")
if isinstance(user_ids, list) and self.config.user_id in user_ids:
return True
return bool(self.config.allow_room_mentions and mentions.get("room") is True)
def _should_process_message(self, room: MatrixRoom, event: RoomMessageText) -> bool:
"""Apply sender and room policy checks before processing Matrix messages."""
if not self.is_allowed(event.sender):
return False
if self._is_direct_room(room):
return True
policy = self.config.group_policy
if policy == "open":
return True
if policy == "allowlist":
return room.room_id in (self.config.group_allow_from or [])
if policy == "mention":
return self._is_bot_mentioned_from_mx_mentions(event)
return False
async def _on_message(self, room: MatrixRoom, event: RoomMessageText) -> None: async def _on_message(self, room: MatrixRoom, event: RoomMessageText) -> None:
# Ignore self messages # Ignore self messages
if event.sender == self.config.user_id: if event.sender == self.config.user_id:
return return
if not self.is_allowed(event.sender): if not self._should_process_message(room, event):
await self._handle_message(
sender_id=event.sender,
chat_id=room.room_id,
content=event.body,
metadata={"room": room.display_name},
)
return return
await self._set_typing(room.room_id, True) await self._set_typing(room.room_id, True)
@@ -286,7 +323,7 @@ class MatrixChannel(BaseChannel):
sender_id=event.sender, sender_id=event.sender,
chat_id=room.room_id, chat_id=room.room_id,
content=event.body, content=event.body,
metadata={"room": room.display_name}, metadata={"room": getattr(room, "display_name", room.room_id)},
) )
except Exception: except Exception:
await self._set_typing(room.room_id, False) await self._set_typing(room.room_id, False)

View File

@@ -1,6 +1,8 @@
"""Configuration schema using Pydantic.""" """Configuration schema using Pydantic."""
from pathlib import Path from pathlib import Path
from typing import Literal
from pydantic import BaseModel, Field, ConfigDict from pydantic import BaseModel, Field, ConfigDict
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
@@ -73,6 +75,9 @@ class MatrixConfig(Base):
# Max seconds to wait for sync_forever to stop gracefully before cancellation fallback. # Max seconds to wait for sync_forever to stop gracefully before cancellation fallback.
sync_stop_grace_seconds: int = 2 sync_stop_grace_seconds: int = 2
allow_from: list[str] = Field(default_factory=list) allow_from: list[str] = Field(default_factory=list)
group_policy: Literal["open", "mention", "allowlist"] = "open"
group_allow_from: list[str] = Field(default_factory=list)
allow_room_mentions: bool = False
class EmailConfig(Base): class EmailConfig(Base):