Skip to content

fix: escape MarkdownV2 reserved chars in Farsi locale strings#872

Open
Matobi98 wants to merge 1 commit into
lnp2pBot:mainfrom
Matobi98:fix/farsi-markdownv2-escape
Open

fix: escape MarkdownV2 reserved chars in Farsi locale strings#872
Matobi98 wants to merge 1 commit into
lnp2pBot:mainfrom
Matobi98:fix/farsi-markdownv2-escape

Conversation

@Matobi98

@Matobi98 Matobi98 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Closes #871

Problem

Farsi (fa) users received no message at all when taking a sell order. The flow stalled silently and the order was cancelled after the timeout with "no invoice was provided". Reported in production for @farhoudif; it worked for everyone else, and the common factor was the Farsi language setting.

Root cause

Several messages are sent with parse_mode: 'MarkdownV2'. In MarkdownV2 the . (period) is a reserved/special character: to use it as literal text you must put a backslash \ in front of it. In the Farsi translations those backslashes were missing (the English/Spanish strings already had them), so Telegram rejected the whole message with:

400: Bad Request: can't parse entities: Character '.' is reserved and must be escaped with the preceding '\'

The .t() calls catch the error and only log it, so the failure is silent — the message (and, for you_took_someone_order, the follow-up Continue button) is never sent, leaving the buyer with nothing.

The subtle part (double backslash)

The translation library (@grammyjs/i18n) runs strings that contain interpolation like ${amount} through a template compiler, and that compiler eats one backslash (it treats \ as an escape char):

  • Writing \. → the compiler turns it into . → reaches Telegram unescaped → 400 error
  • Writing \\. → the compiler turns it into \. → reaches Telegram correctly escaped → works

So the correct escaping depends on whether the string interpolates a variable:

  • String WITH ${...} → use double backslash: \\., \\(, \\)
  • String WITHOUT ${...} → use single backslash: \., \(

Fix

Escaped the reserved characters in locales/fa.yaml for every affected MarkdownV2 key, using the right number of backslashes per the rule above:

Key Interpolated Escaping
you_took_someone_order yes \\. \\( \\)
expired_order yes \\. \\( \\)
dispute_started_channel yes \\. \\( \\)
wizard_add_invoice_exit yes \\.
sell_correct_format no \.
buy_correct_format no \.

Testing

  • Reproduced the exact 400 ... Character '.' is reserved error against the running bot (Farsi account taking a sell order → no message received).
  • Verified the fix by rendering every MarkdownV2 key through the real @grammyjs/i18n library and confirming no unescaped reserved characters remain in the output (not just scanning the YAML source, since the source escaping differs from the rendered result).
  • tsc and eslint clean.

Note

This is not the missing ln_address_temporarily_disabled key (genuinely absent from fa.yaml but harmless, since i18n runs with defaultLanguageOnMissing: true and falls back to English).

Summary by CodeRabbit

  • Documentation
    • Updated Persian help text for buy/sell commands with clearer formatting guidance, including safer quoted parameters, rate adjustments using 0 sats, and fiat ranges.
  • Bug Fixes
    • Fixed several translated messages so special characters render correctly in chats and dispute notices.
    • Improved formatting in order, dispute, wizard exit, and expired-order messages for more consistent display.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR updates the Persian (fa.yaml) locale file, revising sell/buy command format guidance text and escaping Markdown-sensitive characters (parentheses, periods) in several notification templates including dispute, wizard exit, and order-taken messages.

Changes

Persian locale updates

Layer / File(s) Summary
Sell/buy command format guidance
locales/fa.yaml
Updated sell_correct_format and buy_correct_format blocks with new guidance on avoiding bracket characters, using 0 sats with premium/discount for rate computation, and specifying hyphen-separated fiat ranges.
Markdown escaping fixes
locales/fa.yaml
Escaped punctuation and parentheses in you_took_someone_order, dispute_started_channel, wizard_add_invoice_exit, and expired-order dispute-count message templates to fix rendering.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

Possibly related PRs

  • lnp2pBot/bot#711: Both PRs update Persian locales/fa.yaml help/flow text, specifically refining /sell and /buy guidance formatting.

Suggested reviewers: Catrya, grunch, Luquitasjeffrey

Poem

A rabbit hops through Persian text so fine,
Escaping dots and parens, line by line,
Sats set to zero, premiums now clear,
Fiat ranges hyphenated, no more fear,
Hop hop, the messages now render right! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: escaping MarkdownV2-reserved characters in Farsi locale strings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@locales/fa.yaml`:
- Line 89: Remove the escaped hyphens inside the inline code examples in the
locale strings so MarkdownV2 renders them correctly. Update the affected entries
in the fa.yaml translation where the examples use code spans with `\-`, and
replace them with plain hyphens while keeping the rest of the message unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b44037c8-be69-4d6c-ac5f-7adc8eeb493f

📥 Commits

Reviewing files that changed from the base of the PR and between e45b8ec and d5ed5e8.

📒 Files selected for processing (1)
  • locales/fa.yaml

Comment thread locales/fa.yaml

@Luquitasjeffrey Luquitasjeffrey left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@knocte

knocte commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@Matobi98 @Luquitasjeffrey this is the first time I hear that dots needs to be escaped in MarkDown; it doesn't sound realistic sorry; or are we talking about a different dot, like a weird unicode dot-looking character?

@Matobi98

Matobi98 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@Matobi98 @Luquitasjeffrey this is the first time I hear that dots needs to be escaped in MarkDown; it doesn't sound realistic sorry; or are we talking about a different dot, like a weird unicode dot-looking character?

It's the plain ASCII dot, not a weird unicode one. I looked it up: these strings are sent with parse_mode: 'MarkdownV2' (e.g. bot/messages.ts:576), not legacy Markdown. According to the official docs (https://core.telegram.org/bots/api#markdownv2-style), in MarkdownV2 the characters _ * [ ] ( ) ~ > # + - = | { } . ! must be escaped — so the . is reserved there (it isn't in legacy Markdown, which is probably the source of the confusion). If any reserved char is left unescaped, Telegram rejects the whole message with 400: can't parse entities, which is what caused #871. That's what I found so far — let me know if I'm missing something.

@knocte

knocte commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

This is blowing my mind:

Screenshot 2026-07-09 at 08 43 52

Anyway, the solution shouldn't have to use dot escaping in all language files, that's just shooting ourselves in the foot because it's not sustainable, nobody will remember this rule and it will be broken again.

The solution here should be to either not use MarkDownV2 or not use the "parse mode" of MarkDownV2 (not sure if it can be disabled on telegram), or simply replace all dots with escaped dots in the function that performs the localization.

@Matobi98

Copy link
Copy Markdown
Contributor Author

This is blowing my mind:
Screenshot 2026-07-09 at 08 43 52

Anyway, the solution shouldn't have to use dot escaping in all language files, that's just shooting ourselves in the foot because it's not sustainable, nobody will remember this rule and it will be broken again.

The solution here should be to either not use MarkDownV2 or not use the "parse mode" of MarkDownV2 (not sure if it can be disabled on telegram), or simply replace all dots with escaped dots in the function that performs the localization.

You're right that escaping dots across every locale isn't sustainable as a long-term strategy — a centralized approach is the correct end state, and this deserves a dedicated follow-up.

I'd argue it belongs in a separate PR though, for two reasons:

  1. Scope / risk. This PR is a narrow hotfix for Farsi (fa) users receive no message when taking a sell order due to unescaped '.' in MarkdownV2 string #871: the Farsi strings were breaking with 400: can't parse entities and users literally couldn't use those flows. Escaping the reserved chars in fa.yaml fixes the breakage today with a minimal, contained change that touches nothing else.

  2. The centralized fix is a bot-wide change, not a locale fix — and one of the three options doesn't hold as stated.

All three of your suggestions are global changes (they touch parse_mode at the send sites and/or every locale file), so they need their own design + a full re-test across the 10 languages. Concretely:

  • "Escape dots in the localization function" isn't safe with the current architecture. parse_mode is chosen per call site, not in i18n.t — and the bot mixes modes: some messages are sent with legacy Markdown (e.g. bot/messages.ts:101,924), others with MarkdownV2 (e.g. :576), and some with no parse mode. The localization function returns the same string to all of them, so escaping dots there would corrupt every string sent as legacy Markdown/plain, where \. renders as a visible backslash. It also wouldn't fully fix Farsi (fa) users receive no message when taking a sell order due to unescaped '.' in MarkdownV2 string #871: the bug also required escaping ( ) (see this diff), so "replace all dots" alone leaves the parens unescaped and the message still broken. And a blind pass would double-escape already-escaped sequences and inject stray backslashes inside code / links.
  • "Don't use MarkdownV2 / disable parse mode" is a legitimate long-term direction, but it's a cross-cutting refactor: it means changing parse_mode across the ~31 send sites and reworking the intentional formatting (bold, inline /command code, italics) in all 10 language files so they don't render as literal *, , _. That deserves its own PR and QA pass.

So none of the three is a drop-in; they're all bot-wide work. This PR, by contrast, is a contained Farsi-only fix for a message that is broken in production right now.

My proposal: merge this as the immediate fix, and treat the centralized approach (unifying parse mode → then escaping only interpolated values, or moving off MarkdownV2) as a separate PR where it can get the design and multi-language testing it needs.

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.

Farsi (fa) users receive no message when taking a sell order due to unescaped '.' in MarkdownV2 string

3 participants