Repository: python-telegram-bot/python-telegram-bot

Issue:
[Suggestion] Add mention_XXX to Chat class
XXX means .html and .markdown(_v2). It is supposed to mention the Chat in the picked markup. Use title or first_name + space + Lastname as the text if the input parameter name is not set (otherwise use that), and the inline mention of the chat.id.

The logic will be close to the User one, have a look at it here: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/_user.py#L222

Also check out our [contrib](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst) document to get started with this project, and don't forget to unit test your changes!


Discussion:
Hi, i want contribute in this project. Please assign this project to me. 
> Hi, i want contribute in this project. Please assign this project to me @Poolitzer @lorien @iley @zeehio @wjt.

Hi, my contribution to this project has been just one word, fixing a typo. Please do not tag random contributors, it is distracting to us, it is inconsiderate towards our time and we are powerless to help you.

You can check the contributor insights page to find major contributors to a project

https://github.com/python-telegram-bot/python-telegram-bot/graphs/contributors

Have a nice day 

_Edit: Reading again this reply I was too harsh without need. I stand by what I said but I think I could have expressed it with a much better choice of words. I apologize for that and I hope this doesn't discourage you to contribute to this or other open source projects._
@DhwanishShah glad that you like to contribute. I'm assigning this issue to you.
And please indeed don't tag people without good reason ;)
Hi  I am new to this community. Can you please explain in brief about this suggestion @Poolitzer ?
Hey, sure. Please read the description and if you have a specific technical question while implementing the mentioned changes to the Chat class, ask them, and I will explain :D
Hi, i want contribute in this project. Please assign this project to me.
Sicne @DhwanishShah hasn't PRed so far, I'll re-assign to @Srijan-bot . @DhwanishShah if you are in fact preparing a PR, please leave a short comment here.
Can I contribute to this project @Bibo-Joshi. Can you assign me please.
> Sicne @DhwanishShah hasn't PRed so far, I'll re-assign to @Srijan-bot . @DhwanishShah if you are in fact preparing a PR, please leave a short comment here.

Yes I am preparing PR
All right. Can you give an estimate on when you'll be PRin @DhwanishShah? It has been two weeks since your last comment 😉 

@Srijan-bot @Paari1263 please for now have a look at the other [hacktoberfest issues](https://github.com/search?q=org%3Apython-telegram-bot+label%3Ahacktoberfest+is%3Aissue+is%3Aopen) instead.
@DhwanishShah since I've not seen a reply from you since 10 days, I'll unassign you again. @Srijan-bot @Paari1263 (or anyone else) if you're still interested, please leave a short comment. 
`tg://user?id={user_id}` is for mentioning a user.
I cannot find how to mention chat on the Telegram doc.
Hello! I'd like to contribute if that's okay. I'm new to this repo and new to contributing, but I have worked with python quite a bit.
> Hey, sure. Please read the description and if you have a specific technical question while implementing the mentioned changes to the Chat class, ask them, and I will explain :D
@Poolitzer

I don't get the 'mention the Chat in the picked markup'. As mentioned by miles, I can't seem to find anywhere in the API to mention chats, only mentioning users. Could you elaborate on this?


Hi, sorry for my late response. You both are right, there is no documented way of dealing with Chats without usernames, i forgot this when writing the issue.

There is however the link to specific messages in chats with no username, https://t.me/c/chat_id/message_id. This only works if you are part of the chat ofc, and it is only really implemented for super groups/channels. But it can be used to link to a chat without a username.

I need the opinion of the maintainers of this repository before "I" can tell you if/how to implement this. There are three ways to try to abuse this link I can think of, either with adding nothing, with adding -1 or with adding a very large number (this is problematic because some groups might reach this number of messages and then the "chat link" turns into a "message link"). I have collected the behaviour on all official telegram apps:

Normal group links only work in the Android App and the Web apps.

| App | Adding Nothing | Adding -1 | Adding 10000000 |
| :-: | :-: | :-: | :-: |
| Telegram Android | ❌| ✔️ | ✔️|
| Telegram X for Android | ❌<br />(opens the browser)| ❌<br />(opens the browser)| ✔️<br />(error message that message doesn't exist, stays in the last position in the chat)|
| Telegram iOS| ❌| ✔️ | ✔️|
| Telegram Web K| ✔️| ✔️<br />(jumps to the beginning, not to the end)| ✔️|
| Telegram Web X| ✔️<br />(Right now chats don't load their content, so I couldn't verify if they open at the bottom or not)| ✔️| ✔️|
| Telegram Desktop | ❌| ❌<br />(opens the browser)| ✔️|
| Unigram | ❌| ❌| ✔️<br />(stays in the last position in the chat)|

Until we reached a consent if and if yes, how to implement this, you could implement mentioning by chat username. This works with https://t.me/username, in the respective markup. @miles170 commented earlier, so if they want to implement this, I would give them a day to say so.
Alright, no problem! I'll check in on this tomorrow. 
@Poolitzer https://bugs.telegram.org/c/21352 got created. Ideally we get a proper documented way to mention chats without usernames at some point.
Tbh I had thought that the main use case here would be private chats, where mentioning the chat boils down to mentioning the user. IMO it would be perfectly fine to just `return None` (or raise an exception) if the chat is not private until we have a documented way of mentioning chats.

In any case, `Chat.mention_*` should coincide with `User.mention_*` for private chats IMO.
I would prefer the method returns the t.me link for public chats instead of None.
Agree with poolitzer for public chats. For private chats we can just use `tg://user?id=` syntax
> I would prefer the method returns the t.me link for public chats instead of None.

For public chats, sure. For private group-chats, I'm leaning towards returning `None` rather than relying on not-really-supported links 🤔 

> For private chats we can just use `tg://user?id=` syntax

That's exactly what `User.mention_*` does, yes :)




Hello, is it possible I work on this issue? 🙂
We have reached a decision. Please implement the return function the following way: 
If the chat type is PRIVATE, make the function return the usual inline text link.

If not and the chat has a username, make the text link to https://t.me/username.

If the chat does not have a username, raise a TypeError with the text set to something like "Can not create a link to a private group chat".

If you want to implement this @MRZHD1 I will assign you, everyone else had enough time to speak up :D
I can work on this if no one wants to take over.

Looks like it's yours.
I have one question left to implement this. Should I change `mention_html` in `python-telegram-bot/telegram/helpers.py`
```python
def mention_html(user_id: Union[int, str], name: str) -> str
```
to
```python
def mention_html(user_id: Union[int, str], name: str, tg_link_prefix: str = "tg://user?id=") -> str
```

Since `mention_XXX` in chat might use it like `mention_html(id, name, tg_link_prefix="https://t.me/")`

Or should we implement it inside the chat class?
Currently the .link attributes of chat and user do not allow to change the prefix, I wouldn't add it in this PR.

If you think it's a good thing to have that, you can make a new issue, and we can add it everywhere at once.

Resolving patch:
diff --git a/telegram/_chat.py b/telegram/_chat.py
--- a/telegram/_chat.py
+++ b/telegram/_chat.py
@@ -19,6 +19,7 @@
 # along with this program.  If not, see [http://www.gnu.org/licenses/].
 """This module contains an object that represents a Telegram Chat."""
 from datetime import datetime
+from html import escape
 from typing import TYPE_CHECKING, ClassVar, List, Optional, Tuple, Union
 
 from telegram import constants
@@ -30,6 +31,9 @@
 from telegram._utils import enum
 from telegram._utils.defaultvalue import DEFAULT_NONE
 from telegram._utils.types import DVInput, FileInput, JSONDict, ODVInput, ReplyMarkup
+from telegram.helpers import escape_markdown
+from telegram.helpers import mention_html as helpers_mention_html
+from telegram.helpers import mention_markdown as helpers_mention_markdown
 
 if TYPE_CHECKING:
     from telegram import (
@@ -355,6 +359,107 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]:
 
         return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs)
 
+    def mention_markdown(self, name: str = None) -> str:
+        """
+        Note:
+            :tg-const:`telegram.constants.ParseMode.MARKDOWN` is a legacy mode, retained by
+            Telegram for backward compatibility. You should use :meth:`mention_markdown_v2`
+            instead.
+
+        .. versionadded:: 20.0
+
+        Args:
+            name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`.
+
+        Returns:
+            :obj:`str`: The inline mention for the chat as markdown (version 1).
+
+        Raises:
+            :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name`
+                nor the :attr:`first_name` is set, then throw an :exc:`TypeError`.
+                If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title`
+                is set, then throw an :exc:`TypeError`. If chat is a private group chat, then
+                throw an :exc:`TypeError`.
+
+        """
+        if self.type == self.PRIVATE:
+            if name:
+                return helpers_mention_markdown(self.id, name)
+            if self.full_name:
+                return helpers_mention_markdown(self.id, self.full_name)
+            raise TypeError("Can not create a mention to a private chat without first name")
+        if self.username:
+            if name:
+                return f"[{name}]({self.link})"
+            if self.title:
+                return f"[{self.title}]({self.link})"
+            raise TypeError("Can not create a mention to a public chat without title")
+        raise TypeError("Can not create a mention to a private group chat")
+
+    def mention_markdown_v2(self, name: str = None) -> str:
+        """
+        .. versionadded:: 20.0
+
+        Args:
+            name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`.
+
+        Returns:
+            :obj:`str`: The inline mention for the chat as markdown (version 2).
+
+        Raises:
+            :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name`
+                nor the :attr:`first_name` is set, then throw an :exc:`TypeError`.
+                If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title`
+                is set, then throw an :exc:`TypeError`. If chat is a private group chat, then
+                throw an :exc:`TypeError`.
+
+        """
+        if self.type == self.PRIVATE:
+            if name:
+                return helpers_mention_markdown(self.id, name, version=2)
+            if self.full_name:
+                return helpers_mention_markdown(self.id, self.full_name, version=2)
+            raise TypeError("Can not create a mention to a private chat without first name")
+        if self.username:
+            if name:
+                return f"[{escape_markdown(name, version=2)}]({self.link})"
+            if self.title:
+                return f"[{escape_markdown(self.title, version=2)}]({self.link})"
+            raise TypeError("Can not create a mention to a public chat without title")
+        raise TypeError("Can not create a mention to a private group chat")
+
+    def mention_html(self, name: str = None) -> str:
+        """
+        .. versionadded:: 20.0
+
+        Args:
+            name (:obj:`str`): The name used as a link for the chat. Defaults to :attr:`full_name`.
+
+        Returns:
+            :obj:`str`: The inline mention for the chat as HTML.
+
+        Raises:
+            :exc:`TypeError`: If the chat is a private chat and neither the :paramref:`name`
+                nor the :attr:`first_name` is set, then throw an :exc:`TypeError`.
+                If the chat is a public chat and neither the :paramref:`name` nor the :attr:`title`
+                is set, then throw an :exc:`TypeError`. If chat is a private group chat, then
+                throw an :exc:`TypeError`.
+
+        """
+        if self.type == self.PRIVATE:
+            if name:
+                return helpers_mention_html(self.id, name)
+            if self.full_name:
+                return helpers_mention_html(self.id, self.full_name)
+            raise TypeError("Can not create a mention to a private chat without first name")
+        if self.username:
+            if name:
+                return f'<a href="{self.link}">{escape(name)}</a>'
+            if self.title:
+                return f'<a href="{self.link}">{escape(self.title)}</a>'
+            raise TypeError("Can not create a mention to a public chat without title")
+        raise TypeError("Can not create a mention to a private group chat")
+
     async def leave(
         self,
         *,


Test patch:
diff --git a/tests/test_chat.py b/tests/test_chat.py
--- a/tests/test_chat.py
+++ b/tests/test_chat.py
@@ -21,6 +21,7 @@
 
 from telegram import Bot, Chat, ChatLocation, ChatPermissions, Location, User
 from telegram.constants import ChatAction, ChatType
+from telegram.helpers import escape_markdown
 from tests.conftest import check_defaults_handling, check_shortcut_call, check_shortcut_signature
 
 
@@ -880,6 +881,99 @@ async def make_assertion(*_, **kwargs):
         monkeypatch.setattr(chat.get_bot(), "decline_chat_join_request", make_assertion)
         assert await chat.decline_join_request(user_id=42)
 
+    def test_mention_html(self):
+        with pytest.raises(TypeError, match="Can not create a mention to a private group chat"):
+            chat = Chat(id=1, type="foo")
+            chat.mention_html()
+
+        expected = '<a href="tg://user?id={}">{}</a>'
+        chat = Chat(
+            id=1, type=Chat.PRIVATE, first_name="first\u2022name", last_name="last\u2022name"
+        )
+        assert chat.mention_html("the_name*\u2022") == expected.format(chat.id, "the_name*\u2022")
+        assert chat.mention_html() == expected.format(chat.id, chat.full_name)
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a private chat without first name"
+        ):
+            chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name")
+            chat.mention_html()
+
+        expected = '<a href="https://t.me/{}">{}</a>'
+        chat = Chat(id=1, type="foo", username="user\u2022name", title="\u2022title")
+        assert chat.mention_html("the_name*\u2022") == expected.format(
+            chat.username, "the_name*\u2022"
+        )
+        assert chat.mention_html() == expected.format(chat.username, chat.title)
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a public chat without title"
+        ):
+            chat = Chat(id=1, type="foo", username="user\u2022name")
+            chat.mention_html()
+
+    def test_mention_markdown(self):
+        with pytest.raises(TypeError, match="Can not create a mention to a private group chat"):
+            chat = Chat(id=1, type="foo")
+            chat.mention_markdown()
+
+        expected = "[{}](tg://user?id={})"
+        chat = Chat(
+            id=1, type=Chat.PRIVATE, first_name="first\u2022name", last_name="last\u2022name"
+        )
+        assert chat.mention_markdown("the_name*\u2022") == expected.format(
+            "the_name*\u2022", chat.id
+        )
+        assert chat.mention_markdown() == expected.format(chat.full_name, chat.id)
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a private chat without first name"
+        ):
+            chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name")
+            chat.mention_markdown()
+
+        expected = "[{}](https://t.me/{})"
+        chat = Chat(id=1, type="foo", username="user\u2022name", title="\u2022title")
+        assert chat.mention_markdown("the_name*\u2022") == expected.format(
+            "the_name*\u2022", chat.username
+        )
+        assert chat.mention_markdown() == expected.format(chat.title, chat.username)
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a public chat without title"
+        ):
+            chat = Chat(id=1, type="foo", username="user\u2022name")
+            chat.mention_markdown()
+
+    def test_mention_markdown_v2(self):
+        with pytest.raises(TypeError, match="Can not create a mention to a private group chat"):
+            chat = Chat(id=1, type="foo")
+            chat.mention_markdown_v2()
+
+        expected = "[{}](tg://user?id={})"
+        chat = Chat(id=1, type=Chat.PRIVATE, first_name="first{name", last_name="last_name")
+        assert chat.mention_markdown_v2("the{name>\u2022") == expected.format(
+            "the\\{name\\>\u2022", chat.id
+        )
+        assert chat.mention_markdown_v2() == expected.format(
+            escape_markdown(chat.full_name, version=2), chat.id
+        )
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a private chat without first name"
+        ):
+            chat = Chat(id=1, type=Chat.PRIVATE, last_name="last_name")
+            chat.mention_markdown_v2()
+
+        expected = "[{}](https://t.me/{})"
+        chat = Chat(id=1, type="foo", username="user{name", title="{title")
+        assert chat.mention_markdown_v2("the{name>\u2022") == expected.format(
+            "the\\{name\\>\u2022", chat.username
+        )
+        assert chat.mention_markdown_v2() == expected.format(
+            escape_markdown(chat.title, version=2), chat.username
+        )
+        with pytest.raises(
+            TypeError, match="Can not create a mention to a public chat without title"
+        ):
+            chat = Chat(id=1, type="foo", username="user\u2022name")
+            chat.mention_markdown_v2()
+
     def test_equality(self):
         a = Chat(self.id_, self.title, self.type_)
         b = Chat(self.id_, self.title, self.type_)