From 01cf01b629227e4c4b9854989e04cdfcb92e50b4 Mon Sep 17 00:00:00 2001 From: h Date: Wed, 14 Oct 2026 05:06:35 +0200 Subject: [PATCH] fix(backend): rich messages of the new telegram layer read as plain text instead of no text --- backend/src/utils/read/message_view.py | 3 ++ backend/src/utils/read/rich_text.py | 61 ++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 backend/src/utils/read/rich_text.py diff --git a/backend/src/utils/read/message_view.py b/backend/src/utils/read/message_view.py index 0c547c8..5cf932c 100644 --- a/backend/src/utils/read/message_view.py +++ b/backend/src/utils/read/message_view.py @@ -22,6 +22,7 @@ from utils.read.models import ( StickerView, WebPageView, ) +from utils.read.rich_text import rich_text _MEDIA_KEYS = ( "photo", @@ -382,6 +383,8 @@ def build_message_view( row: asyncpg.Record, raw: dict[str, Any], media: list[MediaRef] ) -> MessageView: base = _base_fields(row) + if base["text"] is None: + base["text"] = rich_text(raw.get("rich_message")) via_bot = raw.get("via_bot") sticker = _sticker(raw) try: diff --git a/backend/src/utils/read/rich_text.py b/backend/src/utils/read/rich_text.py new file mode 100644 index 0000000..128f28e --- /dev/null +++ b/backend/src/utils/read/rich_text.py @@ -0,0 +1,61 @@ +from typing import Any + +CELL_GAP = " | " +_TEXT_KEYS = ("summary", "text", "expression", "credit") +_INLINE_KEYS = ("text", "alternative_text", "expression") + + +def _button(button: dict[str, Any]) -> str: + label = _inline(button.get("text")) + url = button.get("url") + return f"{label} ({url})" if isinstance(url, str) else label + + +def _inline(value: object) -> str: + if isinstance(value, str): + return value + if isinstance(value, list): + return "".join(_inline(item) for item in value) + if not isinstance(value, dict): + return "" + button = value.get("button") + if isinstance(button, dict): + return _button(button) + return next((_inline(value[key]) for key in _INLINE_KEYS if key in value), "") + + +def _children(block: dict[str, Any], key: str) -> list[Any]: + value = block.get(key) + return value if isinstance(value, list) else [] + + +def _row(row: object) -> str: + cells = row if isinstance(row, list) else [] + return CELL_GAP.join( + _inline(cell.get("text")) for cell in cells if isinstance(cell, dict) + ) + + +def _block(block: object) -> list[str]: + if not isinstance(block, dict): + return [] + lines = [_inline(block[key]) for key in _TEXT_KEYS if key in block] + lines += [_row(row) for row in _children(block, "cells")] + lines += [ + _button(button) + for button in _children(block, "buttons") + if isinstance(button, dict) + ] + for key in ("items", "blocks"): + for child in _children(block, key): + lines += _block(child) + lines += _block(block.get("caption")) + lines = [line for line in lines if line] + label = block.get("label") + if isinstance(label, str) and lines: + lines[0] = f"{label} {lines[0]}" + return lines + + +def rich_text(rich: object) -> str | None: + return "\n".join(_block(rich)) or None