diff --git a/aiogram/api/types/parse_mode.py b/aiogram/api/types/parse_mode.py
new file mode 100644
index 00000000..873cfe2a
--- /dev/null
+++ b/aiogram/api/types/parse_mode.py
@@ -0,0 +1,13 @@
+from aiogram.utils.helper import Helper, Item
+
+
+class ParseMode(Helper):
+ """
+ Parse mode
+
+ Source: https://core.telegram.org/bots/api#formatting-options
+ """
+
+ HTML = Item("HTML")
+ MARKDOWN = Item("Markdown")
+ MARKDOWN_V2 = Item("MarkdownV2")
diff --git a/aiogram/api/types/user.py b/aiogram/api/types/user.py
index f68c8fb7..b6500ff6 100644
--- a/aiogram/api/types/user.py
+++ b/aiogram/api/types/user.py
@@ -2,7 +2,9 @@ from __future__ import annotations
from typing import Optional
+from ...utils import markdown
from .base import TelegramObject
+from .parse_mode import ParseMode
class User(TelegramObject):
@@ -46,3 +48,25 @@ class User(TelegramObject):
Get user's profile url.
"""
return f"tg://user?id={self.id}"
+
+ def get_mention(self, name: Optional[str] = None, as_html: Optional[bool] = None) -> str:
+ """
+ Get user's mention url.
+
+ :param name: Name of user in the mention link. User's full_name property will be used if not specified.
+ Defaults to None
+ :param as_html: Boolean flag defining format of the resulting mention. Bot parse_mode property will be used if
+ not specified. Defaults to None
+ """
+ if (
+ as_html is None
+ and self.bot.parse_mode
+ and self.bot.parse_mode.upper() == ParseMode.HTML
+ ):
+ as_html = True
+
+ if name is None:
+ name = self.full_name
+ if as_html:
+ return markdown.hlink(name, self.url)
+ return markdown.link(name, self.url)
diff --git a/aiogram/utils/markdown.py b/aiogram/utils/markdown.py
index 5daae546..688e1e16 100644
--- a/aiogram/utils/markdown.py
+++ b/aiogram/utils/markdown.py
@@ -1,11 +1,11 @@
from .text_decorations import html_decoration, markdown_decoration
-def _join(*content, sep=" "):
+def _join(*content: str, sep: str = " ") -> str:
return sep.join(map(str, content))
-def text(*content, sep=" "):
+def text(*content: str, sep: str = " ") -> str:
"""
Join all elements with a separator
@@ -16,7 +16,7 @@ def text(*content, sep=" "):
return _join(*content, sep=sep)
-def bold(*content, sep=" "):
+def bold(*content: str, sep: str = " ") -> str:
"""
Make bold text (Markdown)
@@ -27,7 +27,7 @@ def bold(*content, sep=" "):
return markdown_decoration.bold(value=html_decoration.quote(_join(*content, sep=sep)))
-def hbold(*content, sep=" "):
+def hbold(*content: str, sep: str = " ") -> str:
"""
Make bold text (HTML)
@@ -38,7 +38,7 @@ def hbold(*content, sep=" "):
return html_decoration.bold(value=html_decoration.quote(_join(*content, sep=sep)))
-def italic(*content, sep=" "):
+def italic(*content: str, sep: str = " ") -> str:
"""
Make italic text (Markdown)
@@ -49,7 +49,7 @@ def italic(*content, sep=" "):
return markdown_decoration.italic(value=html_decoration.quote(_join(*content, sep=sep)))
-def hitalic(*content, sep=" "):
+def hitalic(*content: str, sep: str = " ") -> str:
"""
Make italic text (HTML)
@@ -60,7 +60,7 @@ def hitalic(*content, sep=" "):
return html_decoration.italic(value=html_decoration.quote(_join(*content, sep=sep)))
-def code(*content, sep=" "):
+def code(*content: str, sep: str = " ") -> str:
"""
Make mono-width text (Markdown)
@@ -71,7 +71,7 @@ def code(*content, sep=" "):
return markdown_decoration.code(value=html_decoration.quote(_join(*content, sep=sep)))
-def hcode(*content, sep=" "):
+def hcode(*content: str, sep: str = " ") -> str:
"""
Make mono-width text (HTML)
@@ -82,7 +82,7 @@ def hcode(*content, sep=" "):
return html_decoration.code(value=html_decoration.quote(_join(*content, sep=sep)))
-def pre(*content, sep="\n"):
+def pre(*content: str, sep: str = "\n") -> str:
"""
Make mono-width text block (Markdown)
@@ -93,7 +93,7 @@ def pre(*content, sep="\n"):
return markdown_decoration.pre(value=html_decoration.quote(_join(*content, sep=sep)))
-def hpre(*content, sep="\n"):
+def hpre(*content: str, sep: str = "\n") -> str:
"""
Make mono-width text block (HTML)
@@ -104,7 +104,7 @@ def hpre(*content, sep="\n"):
return html_decoration.pre(value=html_decoration.quote(_join(*content, sep=sep)))
-def underline(*content, sep=" "):
+def underline(*content: str, sep: str = " ") -> str:
"""
Make underlined text (Markdown)
@@ -115,7 +115,7 @@ def underline(*content, sep=" "):
return markdown_decoration.underline(value=markdown_decoration.quote(_join(*content, sep=sep)))
-def hunderline(*content, sep=" "):
+def hunderline(*content: str, sep: str = " ") -> str:
"""
Make underlined text (HTML)
@@ -126,7 +126,7 @@ def hunderline(*content, sep=" "):
return html_decoration.underline(value=html_decoration.quote(_join(*content, sep=sep)))
-def strikethrough(*content, sep=" "):
+def strikethrough(*content: str, sep: str = " ") -> str:
"""
Make strikethrough text (Markdown)
@@ -139,7 +139,7 @@ def strikethrough(*content, sep=" "):
)
-def hstrikethrough(*content, sep=" "):
+def hstrikethrough(*content: str, sep: str = " ") -> str:
"""
Make strikethrough text (HTML)
diff --git a/tests/test_api/test_types/test_user.py b/tests/test_api/test_types/test_user.py
index a898cd8e..9a89b05b 100644
--- a/tests/test_api/test_types/test_user.py
+++ b/tests/test_api/test_types/test_user.py
@@ -1,5 +1,9 @@
+from unittest import mock
+from unittest.mock import PropertyMock, _patch
+
import pytest
+from aiogram import Bot
from aiogram.api.types import User
@@ -36,3 +40,28 @@ class TestUser:
def test_url(self, user_id: int, expected_url: str):
user = User(id=user_id, is_bot=False, first_name="User", last_name="Name")
assert user.url == expected_url
+
+ @pytest.mark.parametrize(
+ "name, as_html, bot_parse_mode, expected_mention",
+ [
+ ["Markdown User", False, None, "[Markdown User](tg://user?id=42)"],
+ ["HTML User", True, None, 'HTML User'],
+ ["HTML Bot", None, "HTML", 'HTML Bot'],
+ ["Lowercase html", None, "html", 'Lowercase html'],
+ ["MarkdownV2 Bot", None, "MarkdownV2", "[MarkdownV2 Bot](tg://user?id=42)"],
+ [None, True, "Markdown", 'User Name'],
+ [None, False, "Markdown", "[User Name](tg://user?id=42)"],
+ ],
+ )
+ @mock.patch("aiogram.api.types.User.bot", new_callable=PropertyMock)
+ def test_get_mention(
+ self,
+ mock_bot_property: _patch,
+ name: str,
+ as_html: bool,
+ bot_parse_mode: str,
+ expected_mention: str,
+ ):
+ user = User(id=42, is_bot=False, first_name="User", last_name="Name")
+ mock_bot_property.return_value = Bot(token="42:TEST", parse_mode=bot_parse_mode)
+ assert user.get_mention(name, as_html) == expected_mention