diff --git a/src/bub/channels/message.py b/src/bub/channels/message.py index 3fd3f1ef..322d640d 100644 --- a/src/bub/channels/message.py +++ b/src/bub/channels/message.py @@ -46,6 +46,9 @@ class ChannelMessage: media: list[MediaItem] = field(default_factory=list) lifespan: contextlib.AbstractAsyncContextManager | None = None output_channel: str = "" + # What the sender wrote, for hooks that search or index messages, when ``content`` wraps it with channel + # metadata; ``None`` means ``content`` is that text. + text: str | None = None def __post_init__(self) -> None: self.context.update({"channel": "$" + self.channel, "chat_id": self.chat_id}) @@ -66,5 +69,8 @@ def from_batch(cls, batch: list[ChannelMessage]) -> ChannelMessage: raise ValueError("Batch cannot be empty") template = batch[-1] content = "\n".join(message.content for message in batch) + text = None + if any(message.text is not None for message in batch): + text = "\n".join(message.content if message.text is None else message.text for message in batch) media = [item for message in batch for item in message.media] - return replace(template, content=content, media=media) + return replace(template, content=content, media=media, text=text) diff --git a/src/bub/channels/telegram.py b/src/bub/channels/telegram.py index c455de4a..38e06d89 100644 --- a/src/bub/channels/telegram.py +++ b/src/bub/channels/telegram.py @@ -258,13 +258,15 @@ async def _build_message(self, message: Message) -> ChannelMessage: metadata["reply_to_message"] = reply_meta reply_media = _extract_media_items(reply_meta) media_items.extend(reply_media) - content = json.dumps({"message": content, **metadata}, ensure_ascii=False) + text = content + content = json.dumps({"message": text, **metadata}, ensure_ascii=False) is_active = MESSAGE_FILTER.filter(message) is not False return ChannelMessage( session_id=session_id, channel=self.name, chat_id=chat_id, content=content, + text=text, media=media_items, is_active=is_active, lifespan=self.start_typing(chat_id), diff --git a/tests/test_channels.py b/tests/test_channels.py index 2e17d965..5c271d43 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -317,6 +317,14 @@ async def receive(message: ChannelMessage) -> None: assert handled == [",help\nhello"] +def test_channel_message_batch_joins_text_and_falls_back_to_content() -> None: + wrapped = ChannelMessage(session_id="telegram:42", channel="telegram", content='{"message": "hello"}', text="hello") + plain = ChannelMessage(session_id="telegram:42", channel="telegram", content="world") + + assert ChannelMessage.from_batch([wrapped, plain]).text == "hello\nworld" + assert ChannelMessage.from_batch([plain, plain]).text is None + + @pytest.mark.asyncio async def test_channel_manager_dispatch_uses_output_channel_and_preserves_metadata(load_config) -> None: _load_channel_config(load_config, enabled_channels="cli") @@ -1871,6 +1879,7 @@ async def test_telegram_channel_build_message_wraps_payload_and_disables_outboun assert result.is_active is True assert '"message": "hello"' in result.content assert '"reply_to_message"' in result.content + assert result.text == "hello" assert result.lifespan is not None