Repository: python-telegram-bot/python-telegram-bot (Python)

Issue:
[BUG] Invalid MarkdownV1 parsing
### Steps to Reproduce

1. Call the `mention_markdown(123, "Keeping_the_underscores", version=1)` function from `telegram.helpers`

### Expected behaviour

`[Keeping_the_underscores](tg://user?id=123)`, that is valid MarkdownV1 and consistent with https://core.telegram.org/bots/api#markdown-style.

### Actual behaviour

`[Keeping\\_the\\_underscores](tg://user?id=123)`. However this will fail to parse into well-formed MarkdownV1 -- in fact Telegram will respond with an error if it occurs in a message with `parse_mode = Markdown`.

### Operating System

Linux

### Version of Python, python-telegram-bot & dependencies

```shell
python-telegram-bot==20.0a2
```


### Relevant log output

_No response_

### Additional Context

_No response_


Discussion:
Hi. Thanks for the catch. I can reproduce that the additional escaping produces the wrong result. What I can't reproduce is that TG raises an error. Can you provide a minimal reproducable example for that?
Hello, sorry took a couple of days off, indeed I could not reproduce the error part of this Issue. Must have run into an unrelated issue when testing. 

To recap: 
- `[Keeping\\_the\\_underscore](URL)` (what this framework I think was trying to do) is the correct escaping but not needed in that specific context
-  `[Keeping\_the\_underscore](URL)` (what it is actually doing) is not a correct escaping

Thanks!
There's a pull request open for this at #3155. @why-not-try-calmer could you double check if that fixes your use case?
You can install the patched version via `pip install git+https://https://github.com/python-telegram-bot/python-telegram-bot.git@fix_escape_mk1`



Patch:
diff --git a/telegram/helpers.py b/telegram/helpers.py
index 73ca3a67024..ac7e1936ae1 100644
--- a/telegram/helpers.py
+++ b/telegram/helpers.py
@@ -93,7 +93,10 @@ def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> s
     Returns:
         :obj:`str`: The inline mention for the user as Markdown.
     """
-    return f"[{escape_markdown(name, version=version)}](tg://user?id={user_id})"
+    tg_link = f"tg://user?id={user_id}"
+    if version == 1:
+        return f"[{name}]({tg_link})"
+    return f"[{escape_markdown(name, version=version)}]({tg_link})"
 
 
 def effective_message_type(entity: Union["Message", "Update"]) -> Optional[str]:


Test patch:
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 055f174675b..fa1f2a40cf9 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -25,32 +25,50 @@
 
 
 class TestHelpers:
-    def test_escape_markdown(self):
-        test_str = "*bold*, _italic_, `code`, [text_link](http://github.com/)"
-        expected_str = r"\*bold\*, \_italic\_, \`code\`, \[text\_link](http://github.com/)"
-
-        assert expected_str == helpers.escape_markdown(test_str)
-
-    def test_escape_markdown_v2(self):
-        test_str = r"a_b*c[d]e (fg) h~I`>JK#L+MN -O=|p{qr}s.t!\ \u"
-        expected_str = r"a\_b\*c\[d\]e \(fg\) h\~I\`\>JK\#L\+MN \-O\=\|p\{qr\}s\.t\!\\ \\u"
-
-        assert expected_str == helpers.escape_markdown(test_str, version=2)
-
-    def test_escape_markdown_v2_monospaced(self):
-
-        test_str = r"mono/pre: `abc` \int (`\some \`stuff)"
-        expected_str = "mono/pre: \\`abc\\` \\\\int (\\`\\\\some \\\\\\`stuff)"
-
-        assert expected_str == helpers.escape_markdown(
+    @pytest.mark.parametrize(
+        "test_str,expected",
+        [
+            ("*bold*", r"\*bold\*"),
+            ("_italic_", r"\_italic\_"),
+            ("`code`", r"\`code\`"),
+            ("[text_link](https://github.com/)", r"\[text\_link](https://github.com/)"),
+        ],
+        ids=["bold", "italic", "code", "text_link"],
+    )
+    def test_escape_markdown(self, test_str, expected):
+        assert expected == helpers.escape_markdown(test_str)
+
+    @pytest.mark.parametrize(
+        "test_str, expected",
+        [
+            (r"a_b*c[d]e", r"a\_b\*c\[d\]e"),
+            (r"(fg) ", r"\(fg\) "),
+            (r"h~I`>JK#L+MN", r"h\~I\`\>JK\#L\+MN"),
+            (r"-O=|p{qr}s.t!\ ", r"\-O\=\|p\{qr\}s\.t\!\\ "),
+            (r"\u", r"\\u"),
+        ],
+    )
+    def test_escape_markdown_v2(self, test_str, expected):
+        assert expected == helpers.escape_markdown(test_str, version=2)
+
+    @pytest.mark.parametrize(
+        "test_str, expected",
+        [
+            (r"mono/pre:", r"mono/pre:"),
+            ("`abc`", r"\`abc\`"),
+            (r"\int", r"\\int"),
+            (r"(`\some \` stuff)", r"(\`\\some \\\` stuff)"),
+        ],
+    )
+    def test_escape_markdown_v2_monospaced(self, test_str, expected):
+        assert expected == helpers.escape_markdown(
             test_str, version=2, entity_type=MessageEntity.PRE
         )
-        assert expected_str == helpers.escape_markdown(
+        assert expected == helpers.escape_markdown(
             test_str, version=2, entity_type=MessageEntity.CODE
         )
 
     def test_escape_markdown_v2_text_link(self):
-
         test_str = "https://url.containing/funny)cha)\\ra\\)cter\\s"
         expected_str = "https://url.containing/funny\\)cha\\)\\\\ra\\\\\\)cter\\\\s"
 
@@ -59,8 +77,10 @@ def test_escape_markdown_v2_text_link(self):
         )
 
     def test_markdown_invalid_version(self):
-        with pytest.raises(ValueError):
+        with pytest.raises(ValueError, match="Markdown version must be either"):
             helpers.escape_markdown("abc", version=-1)
+        with pytest.raises(ValueError, match="Markdown version must be either"):
+            helpers.mention_markdown(1, "abc", version=-1)
 
     def test_create_deep_linked_url(self):
         username = "JamesTheMock"
@@ -124,12 +144,20 @@ def test_mention_html(self):
 
         assert expected == helpers.mention_html(1, "the name")
 
-    def test_mention_markdown(self):
-        expected = "[the name](tg://user?id=1)"
-
-        assert expected == helpers.mention_markdown(1, "the name")
+    @pytest.mark.parametrize(
+        "test_str, expected",
+        [
+            ("the name", "[the name](tg://user?id=1)"),
+            ("under_score", "[under_score](tg://user?id=1)"),
+            ("starred*text", "[starred*text](tg://user?id=1)"),
+            ("`backtick`", "[`backtick`](tg://user?id=1)"),
+            ("[square brackets", "[[square brackets](tg://user?id=1)"),
+        ],
+    )
+    def test_mention_markdown(self, test_str, expected):
+        assert expected == helpers.mention_markdown(1, test_str)
 
     def test_mention_markdown_2(self):
         expected = r"[the\_name](tg://user?id=1)"
 
-        assert expected == helpers.mention_markdown(1, "the_name")
+        assert expected == helpers.mention_markdown(1, "the_name", 2)
diff --git a/tests/test_user.py b/tests/test_user.py
index f077debd374..8f6b0cf1a4f 100644
--- a/tests/test_user.py
+++ b/tests/test_user.py
@@ -534,7 +534,7 @@ def test_mention_markdown(self, user):
 
         assert user.mention_markdown() == expected.format(user.full_name, user.id)
         assert user.mention_markdown("the_name*\u2022") == expected.format(
-            "the\\_name\\*\u2022", user.id
+            "the_name*\u2022", user.id
         )
         assert user.mention_markdown(user.username) == expected.format(user.username, user.id)