Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion mautrix_googlechat/formatter/from_matrix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,65 @@
from __future__ import annotations

import html
import logging

from maugclib import googlechat_pb2 as googlechat
from mautrix.types import Format, TextMessageEventContent
from mautrix.types import Format, TextMessageEventContent, UserID

from ... import puppet as pu
from ..util import FormatError, add_surrogate, del_surrogate
from .gc_message import GCEntity, GCEntityType
from .parser import MX_ROOM_MENTION, parse_html

log = logging.getLogger("mau.fmt.matrix")


async def _annotations_from_m_mentions(
body: str,
mentions: dict,
) -> list[googlechat.Annotation] | None:
"""Build USER_MENTION annotations from MSC3952 m.mentions when formatted_body is absent."""
user_ids = mentions.get("user_ids")
if not user_ids:
return None
annotations = []
for mxid in user_ids:
gcid = pu.Puppet.get_id_from_mxid(UserID(mxid))
if not gcid:
continue
puppet = await pu.Puppet.get_by_gcid(gcid, create=False)
display_name = puppet.name if puppet else None
# Find the mention text in the body. Clients typically render as @DisplayName.
offset = -1
if display_name:
mention_text = f"@{display_name}"
offset = body.find(mention_text)
if offset == -1:
# Try without @ prefix in case the body has a different format
offset = body.find(display_name)
if offset == -1:
# Display name not found in body — skip this mention rather than
# inject an annotation with wrong offsets.
log.debug(
"Skipping m.mentions entry %s: display name %r not found in body",
mxid,
display_name,
)
continue
length = (
len(mention_text)
if display_name and body[offset:].startswith(f"@{display_name}")
else len(display_name or "")
)
entity = GCEntity(
type=GCEntityType.USER_MENTION,
offset=offset,
length=length,
extra_info={"user_id": gcid, "displayname": display_name},
)
annotations.append(entity.internal)
return annotations or None


async def matrix_to_googlechat(
content: TextMessageEventContent,
Expand All @@ -31,6 +83,11 @@ async def matrix_to_googlechat(
if MX_ROOM_MENTION in content.body:
content.formatted_body = html.escape(content.body)
else:
# No HTML — fall back to m.mentions (MSC3952) for mention annotations.
mentions = content.get("m.mentions", {})
if mentions:
annotations = await _annotations_from_m_mentions(content.body, mentions)
return content.body, annotations
return content.body, None
try:
text, entities = await parse_html(add_surrogate(content.formatted_body))
Expand Down
Loading