Skip to content

Add support for messageRichMessage (tables, lists, code blocks, etc)#552

Open
gabrieltomasin wants to merge 3 commits into
d99kris:masterfrom
gabrieltomasin:feat/rich-message-support
Open

Add support for messageRichMessage (tables, lists, code blocks, etc)#552
gabrieltomasin wants to merge 3 commits into
d99kris:masterfrom
gabrieltomasin:feat/rich-message-support

Conversation

@gabrieltomasin

Copy link
Copy Markdown

Problem

Telegram recently introduced rich messages (messageRichMessage), which support advanced formatting like tables, nested lists, code blocks, collapsible sections, block quotes, and more. These are sent by bots and some clients that use the new rich message API.

Currently, nchat does not handle this message type — these messages fall through to the default else branch in TdMessageContentConvert() and are displayed as: [UnknownMessage -1243148892]

This means any message containing a table, structured list, or other rich content is completely unreadable in nchat.

Solution

This PR adds three private helper functions to TgChat::Impl that recursively convert rich message content to plain text suitable for terminal display:

GetRichText()

Recursively extracts plain text from any RichText variant (bold, italic, strikethrough, links, mentions, custom emoji, etc.), preserving the visible text content while discarding formatting markup.

AppendPageBlock()

Renders PageBlock elements as terminal-friendly plain text:

Block Type Rendering
pageBlockTable ASCII-aligned with +---+ borders, using wcswidth for correct column sizing with UTF-8
pageBlockList Bullets (-) with indentation for nested items
pageBlockPreformatted Preserved as-is with indentation
pageBlockBlockQuote > prefix
pageBlockDetails Header with > (collapsed) or v (open) marker
pageBlockDivider ---
Headers/subheaders/paragraphs Plain text
Collage/slideshow/cover Recursed into sub-blocks

GetRichMessageText()

Iterates over all PageBlocks in a richMessage and assembles the final text string.

UTF-8 display width

Table column widths are calculated using StrUtil::WStringWidth() (which calls wcswidth) rather than std::string::size(), so accented characters and emoji are sized correctly for terminal alignment.

Testing

Built and tested locally against rich messages sent via Telegram bot API. Tables, lists, code blocks, and quotes render correctly in the terminal.

Before:
[UnknownMessage -1243148892]

After (example table):

+----------+---------+---------+
| Col A    | Col B   | Col C   |
+----------+---------+---------+
| Value 1  | Value 2 | Value 3 |
| Value 4  | Value 5 | Value 6 |
+----------+---------+---------+

gabrieltomasin and others added 2 commits July 7, 2026 07:56
nchat was showing [UnknownMessage <id>] for Telegram rich messages
(tables, lists, code blocks, collapsible details, etc) because
TdMessageContentConvert() did not handle the messageRichMessage type.

This adds:
- GetRichText(): recursively extracts plain text from all RichText
  variants (bold, italic, links, mentions, custom emoji, etc)
- AppendPageBlock(): renders PageBlock types as terminal-friendly text:
  - Tables -> ASCII aligned with borders
  - Lists -> bullets with indentation
  - Code blocks -> preserved formatting
  - Block quotes -> '> ' prefix
  - Collapsible details -> header + indented content
  - Dividers, headers, subheaders, paragraphs
- GetRichMessageText(): assembles all blocks into final text
- New else-if clause in TdMessageContentConvert() to dispatch
  messageRichMessage to the new renderer

Co-authored-by: Claude Code <noreply@anthropic.com>
The previous implementation used std::string::size() to calculate column
widths, which returns byte count, not display width. This caused
misaligned tables when cells contained accented characters (é, á, ã) or
emoji (✅), since these take more bytes than display columns.

Now uses StrUtil::ToWString + StrUtil::WStringWidth (wcswidth) to measure
actual terminal display width, and pads cells accordingly.

Also fixes separator line to have leading/trailing '+' for proper border.
@gabrieltomasin
gabrieltomasin marked this pull request as ready for review July 7, 2026 11:32
@d99kris d99kris self-assigned this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants