From 7cec70f94f30192d10b0cee75ad7d3672f8c8722 Mon Sep 17 00:00:00 2001 From: Igor Sereda Date: Sun, 31 May 2020 03:10:40 +0300 Subject: [PATCH] AIOG-T-48. User method get_mention() added * method added * unit tests added * markdown utils typing fixed * ParseMode types class added fixup! AIOG-T-48. User method get_mention() added --- aiogram/api/types/parse_mode.py | 13 ++++++++++++ aiogram/api/types/user.py | 24 +++++++++++++++++++++ aiogram/utils/markdown.py | 28 ++++++++++++------------- tests/test_api/test_types/test_user.py | 29 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 aiogram/api/types/parse_mode.py 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