toTelegramMarkdownV2 function
Convert console_markdown text to Telegram MarkdownV2.
Handles the full conversion pipeline:
- Strips ANSI escape codes
- Extracts code blocks, inline code, and links (preserving them)
- Strips dynamic color and
<console>tags - Parses console_markdown formatting with the
markedpackage - Escapes MarkdownV2 special characters in plain text
- Restores code blocks, links, and formatting delimiters
Implementation
String toTelegramMarkdownV2(String text) {
// Step 1: Strip ANSI escape codes
text = text.replaceAll(RegExp(r'\x1B\[[0-9;]*[a-zA-Z]'), '');
// Step 2: Extract code blocks, inline code, and links into placeholders.
// These need special escaping rules and must not be processed by the
// marked parser.
final extractions = <String, String>{};
var counter = 0;
// Fenced code blocks: ```lang\ncontent```
text = text.replaceAllMapped(RegExp(r'```(\w*\n?)([\s\S]*?)```'), (m) {
final id = counter++;
final lang = m.group(1) ?? '';
final code = _escapeCodeText(m.group(2) ?? '');
extractions[_ext(id)] = '```$lang$code```';
return _ext(id);
});
// Inline code: `content`
text = text.replaceAllMapped(RegExp(r'`([^`\n]+)`'), (m) {
final id = counter++;
final code = _escapeCodeText(m.group(1)!);
extractions[_ext(id)] = '`$code`';
return _ext(id);
});
// Markdown links: [text](url)
text = text.replaceAllMapped(RegExp(r'\[([^\]]+)\]\(([^)]+)\)'), (m) {
final id = counter++;
final linkText = _escapePlainText(m.group(1)!);
final linkUrl = _escapeLinkUrl(m.group(2)!);
extractions[_ext(id)] = '[$linkText]($linkUrl)';
return _ext(id);
});
// Step 3: Strip dynamic color/console tags that the marked parser
// can't handle (complex regex-based patterns in console_markdown).
// <console ...>text</console>
text = text.replaceAllMapped(
RegExp(r'<console[^>]*>([\s\S]*?)</console>'),
(m) => m.group(1)!,
);
// <rgb(r,g,b)>text</rgb>
text = text.replaceAllMapped(
RegExp(r'<rgb\([^)]*\)[^>]*>([\s\S]*?)</rgb>'),
(m) => m.group(1)!,
);
// <hex(...)>text</hex>
text = text.replaceAllMapped(
RegExp(r'<hex\([^)]*\)[^>]*>([\s\S]*?)</hex>'),
(m) => m.group(1)!,
);
// <#color>text</#>
text = text.replaceAllMapped(
RegExp(r'<#[0-9a-fA-F]+[^>]*>([\s\S]*?)</#>'),
(m) => m.group(1)!,
);
// Step 4: Parse with marked (console_markdown patterns → placeholders)
text = _ConsoleToPlaceholders.apply(text);
// Step 5: Escape MarkdownV2 special chars in plain text segments
// (everything between placeholder tokens)
text = _escapeNonPlaceholders(text);
// Step 6: Replace format markers with MarkdownV2 delimiters
for (final entry in _fmtToMdV2.entries) {
text = text.replaceAll(_fmt(entry.key), entry.value);
}
// Step 7: Restore extracted content (code blocks, inline code, links)
for (final entry in extractions.entries) {
text = text.replaceAll(entry.key, entry.value);
}
return text;
}