diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 5a3c80630..fe869f18e 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -430,6 +430,20 @@ class TelegramAdapter(BasePlatformAdapter): # "Message is not modified" — content identical, treat as success if "not modified" in err_str: return SendResult(success=True, message_id=message_id) + # Message too long — content exceeded 4096 chars (e.g. during + # streaming). Truncate and succeed so the stream consumer can + # split the overflow into a new message instead of dying. + if "message_too_long" in err_str or "too long" in err_str: + truncated = content[: self.MAX_MESSAGE_LENGTH - 20] + "…" + try: + await self._bot.edit_message_text( + chat_id=int(chat_id), + message_id=int(message_id), + text=truncated, + ) + except Exception: + pass # best-effort truncation + return SendResult(success=True, message_id=message_id) # Flood control / RetryAfter — back off and retry once retry_after = getattr(e, "retry_after", None) if retry_after is not None or "retry after" in err_str: diff --git a/gateway/stream_consumer.py b/gateway/stream_consumer.py index 1b264c534..2ceb0fb1d 100644 --- a/gateway/stream_consumer.py +++ b/gateway/stream_consumer.py @@ -87,6 +87,10 @@ class GatewayStreamConsumer: async def run(self) -> None: """Async task that drains the queue and edits the platform message.""" + # Platform message length limit — leave room for cursor + formatting + _raw_limit = getattr(self.adapter, "MAX_MESSAGE_LENGTH", 4096) + _safe_limit = max(500, _raw_limit - len(self.cfg.cursor) - 100) + try: while True: # Drain all available items from the queue @@ -112,6 +116,21 @@ class GatewayStreamConsumer: ) if should_edit and self._accumulated: + # Split overflow: if accumulated text exceeds the platform + # limit, finalize the current message and start a new one. + while ( + len(self._accumulated) > _safe_limit + and self._message_id is not None + ): + split_at = self._accumulated.rfind("\n", 0, _safe_limit) + if split_at < _safe_limit // 2: + split_at = _safe_limit + chunk = self._accumulated[:split_at] + await self._send_or_edit(chunk) + self._accumulated = self._accumulated[split_at:].lstrip("\n") + self._message_id = None + self._last_sent_text = "" + display_text = self._accumulated if not got_done: display_text += self.cfg.cursor