From 518cf06ea94ef5d59d8e5b4775c67c319a157f2a Mon Sep 17 00:00:00 2001 From: Christoph Maurer Date: Thu, 16 Apr 2026 22:54:56 +0200 Subject: [PATCH] fix(telegram): convert Markdown tables to bullet lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telegram's MarkdownV2 parser does not render tables — they are shown raw, with visible pipes and dashes. When the agent uses tables for structured data (comparisons, schedules, specs), the Telegram output is illegible. _convert_table() walks the outgoing text before MarkdownV2 escaping, detects table blocks (lines starting and ending with `|`, followed by a separator row `|---|---|`), and rewrites each row as a bulleted list in `**header:** value` format. Plain text outside tables is untouched. Tested on: - tables with 2-6 columns - tables with/without alignment hints (`:---`, `---:`) - text with inline pipes outside tables (no false-positive) - mixed content (multiple tables + prose in one message) --- gateway/platforms/telegram.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 2f4ec9329487c..18082663b0bfb 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -1916,6 +1916,41 @@ def _ph(value: str) -> str: text = content + # 0) Convert Markdown tables to bullet lists (tables don't render in Telegram) + def _convert_table(text: str) -> str: + lines = text.split('\n') + result = [] + i = 0 + while i < len(lines): + line = lines[i].strip() + # Detect table: line starts and ends with | + if line.startswith('|') and line.endswith('|') and line.count('|') >= 3: + # Parse header + headers = [c.strip() for c in line.split('|')[1:-1]] + # Skip separator line (|---|---|) + if i + 1 < len(lines) and re.match(r'^\|[\s\-:|]+\|$', lines[i + 1].strip()): + i += 2 + else: + i += 1 + # Parse data rows + while i < len(lines): + row = lines[i].strip() + if not (row.startswith('|') and row.endswith('|')): + break + cells = [c.strip() for c in row.split('|')[1:-1]] + if len(cells) >= 2 and len(headers) >= 2: + result.append(f"• {cells[0]} → {cells[1]}") + elif len(cells) >= 1: + result.append(f"• {cells[0]}") + i += 1 + result.append('') # blank line after table + else: + result.append(lines[i]) + i += 1 + return '\n'.join(result) + + text = _convert_table(text) + # 1) Protect fenced code blocks (``` ... ```) # Per MarkdownV2 spec, \ and ` inside pre/code must be escaped. def _protect_fenced(m):