toTelegramMarkdownV2 function

String toTelegramMarkdownV2(
  1. String text
)

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 marked package
  • 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;
}