diff --git a/README.md b/README.md
index 0f98be8..aa8b32e 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
A simple, but extensible Python implementation for the Telegram Bot API.
Both synchronous and asynchronous.
-## Supported Bot API version: 6.6!
+##
Supported Bot API version: 6.7!
diff --git a/telebot/__init__.py b/telebot/__init__.py
index c385a29..0579325 100644
--- a/telebot/__init__.py
+++ b/telebot/__init__.py
@@ -3443,6 +3443,40 @@ class TeleBot:
"""
result = apihelper.get_my_commands(self.token, scope, language_code)
return [types.BotCommand.de_json(cmd) for cmd in result]
+
+ def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=None):
+ """
+ Use this method to change the bot's name. Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#setmyname
+
+ :param name: Optional. New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.
+ :type name: :obj:`str`
+
+ :param language_code: Optional. A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose
+ language there is no dedicated name.
+ :type language_code: :obj:`str`
+
+ :return: True on success.
+ """
+
+ return apihelper.set_my_name(self.token, name, language_code)
+
+ def get_my_name(self, language_code: Optional[str]=None):
+ """
+ Use this method to get the current bot name for the given user language.
+ Returns BotName on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#getmyname
+
+ :param language_code: Optional. A two-letter ISO 639-1 language code or an empty string
+ :type language_code: :obj:`str`
+
+ :return: :class:`telebot.types.BotName`
+ """
+
+ result = apihelper.get_my_name(self.token, language_code)
+ return types.BotName.de_json(result)
def set_my_description(self, description: Optional[str]=None, language_code: Optional[str]=None):
"""
@@ -3450,6 +3484,8 @@ class TeleBot:
the chat with the bot if the chat is empty.
Returns True on success.
+ Telegram documentation: https://core.telegram.org/bots/api#setmydescription
+
:param description: New bot description; 0-512 characters. Pass an empty string to remove the dedicated description for the given language.
:type description: :obj:`str`
@@ -3467,6 +3503,8 @@ class TeleBot:
Use this method to get the current bot description for the given user language.
Returns BotDescription on success.
+ Telegram documentation: https://core.telegram.org/bots/api#getmydescription
+
:param language_code: A two-letter ISO 639-1 language code or an empty string
:type language_code: :obj:`str`
@@ -3481,6 +3519,8 @@ class TeleBot:
is sent together with the link when users share the bot.
Returns True on success.
+ Telegram documentation: https://core.telegram.org/bots/api#setmyshortdescription
+
:param short_description: New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language.
:type short_description: :obj:`str`
@@ -3498,6 +3538,8 @@ class TeleBot:
Use this method to get the current bot short description for the given user language.
Returns BotShortDescription on success.
+ Telegram documentation: https://core.telegram.org/bots/api#getmyshortdescription
+
:param language_code: A two-letter ISO 639-1 language code or an empty string
:type language_code: :obj:`str`
@@ -4479,7 +4521,8 @@ class TeleBot:
is_personal: Optional[bool]=None,
next_offset: Optional[str]=None,
switch_pm_text: Optional[str]=None,
- switch_pm_parameter: Optional[str]=None) -> bool:
+ switch_pm_parameter: Optional[str]=None,
+ button: Optional[types.InlineQueryResultsButton]=None) -> bool:
"""
Use this method to send answers to an inline query. On success, True is returned.
No more than 50 results per query are allowed.
@@ -4515,11 +4558,18 @@ class TeleBot:
:param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button
:type switch_pm_text: :obj:`str`
+ :param button: A JSON-serialized object describing a button to be shown above inline query results
+ :type button: :obj:`types.InlineQueryResultsButton`
+
:return: On success, True is returned.
:rtype: :obj:`bool`
"""
+ if not button and (switch_pm_text or switch_pm_parameter):
+ logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.")
+ button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter)
+
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
- switch_pm_text, switch_pm_parameter)
+ button)
def answer_callback_query(
self, callback_query_id: int,
diff --git a/telebot/apihelper.py b/telebot/apihelper.py
index b5eb065..a07f1ef 100644
--- a/telebot/apihelper.py
+++ b/telebot/apihelper.py
@@ -1196,6 +1196,22 @@ def get_my_commands(token, scope=None, language_code=None):
payload['language_code'] = language_code
return _make_request(token, method_url, params=payload)
+def set_my_name(token, name=None, language_code=None):
+ method_url = r'setMyName'
+ payload = {}
+ if name is not None:
+ payload['name'] = name
+ if language_code is not None:
+ payload['language_code'] = language_code
+ return _make_request(token, method_url, params=payload, method='post')
+
+def get_my_name(token, language_code=None):
+ method_url = r'getMyName'
+ payload = {}
+ if language_code is not None:
+ payload['language_code'] = language_code
+ return _make_request(token, method_url, params=payload)
+
def set_chat_menu_button(token, chat_id=None, menu_button=None):
method_url = r'setChatMenuButton'
payload = {}
@@ -1598,7 +1614,7 @@ def answer_callback_query(token, callback_query_id, text=None, show_alert=None,
def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None,
- switch_pm_text=None, switch_pm_parameter=None):
+ button=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': _convert_list_json_serializable(results)}
if cache_time is not None:
@@ -1607,10 +1623,8 @@ def answer_inline_query(token, inline_query_id, results, cache_time=None, is_per
payload['is_personal'] = is_personal
if next_offset is not None:
payload['next_offset'] = next_offset
- if switch_pm_text:
- payload['switch_pm_text'] = switch_pm_text
- if switch_pm_parameter:
- payload['switch_pm_parameter'] = switch_pm_parameter
+ if button is not None:
+ payload["button"] = button.to_json()
return _make_request(token, method_url, params=payload, method='post')
diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py
index ebf5e80..1a86024 100644
--- a/telebot/async_telebot.py
+++ b/telebot/async_telebot.py
@@ -4361,6 +4361,40 @@ class AsyncTeleBot:
result = await asyncio_helper.get_my_commands(self.token, scope, language_code)
return [types.BotCommand.de_json(cmd) for cmd in result]
+ async def set_my_name(self, name: Optional[str]=None, language_code: Optional[str]=None):
+ """
+ Use this method to change the bot's name. Returns True on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#setmyname
+
+ :param name: Optional. New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.
+ :type name: :obj:`str`
+
+ :param language_code: Optional. A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose
+ language there is no dedicated name.
+ :type language_code: :obj:`str`
+
+ :return: True on success.
+ """
+
+ return await asyncio_helper.set_my_name(self.token, name, language_code)
+
+ async def get_my_name(self, language_code: Optional[str]=None):
+ """
+ Use this method to get the current bot name for the given user language.
+ Returns BotName on success.
+
+ Telegram documentation: https://core.telegram.org/bots/api#getmyname
+
+ :param language_code: Optional. A two-letter ISO 639-1 language code or an empty string
+ :type language_code: :obj:`str`
+
+ :return: :class:`telebot.types.BotName`
+ """
+
+ result = await asyncio_helper.get_my_name(self.token, language_code)
+ return types.BotName.de_json(result)
+
async def set_chat_menu_button(self, chat_id: Union[int, str]=None,
menu_button: types.MenuButton=None) -> bool:
"""
@@ -5337,7 +5371,8 @@ class AsyncTeleBot:
is_personal: Optional[bool]=None,
next_offset: Optional[str]=None,
switch_pm_text: Optional[str]=None,
- switch_pm_parameter: Optional[str]=None) -> bool:
+ switch_pm_parameter: Optional[str]=None,
+ button: Optional[types.InlineQueryResultsButton]=None) -> bool:
"""
Use this method to send answers to an inline query. On success, True is returned.
No more than 50 results per query are allowed.
@@ -5373,11 +5408,18 @@ class AsyncTeleBot:
:param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button
:type switch_pm_text: :obj:`str`
+ :param button: A JSON-serialized object describing a button to be shown above inline query results
+ :type button: :obj:`types.InlineQueryResultsButton`
+
:return: On success, True is returned.
:rtype: :obj:`bool`
"""
+
+ if not button and (switch_pm_text or switch_pm_parameter):
+ logger.warning("switch_pm_text and switch_pm_parameter are deprecated for answer_inline_query. Use button instead.")
+ button = types.InlineQueryResultsButton(text=switch_pm_text, start_parameter=switch_pm_parameter)
return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
- switch_pm_text, switch_pm_parameter)
+ button)
async def answer_callback_query(
self, callback_query_id: int,
diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py
index 6f7ef0f..5a615b3 100644
--- a/telebot/asyncio_helper.py
+++ b/telebot/asyncio_helper.py
@@ -1183,6 +1183,23 @@ async def get_my_commands(token, scope=None, language_code=None):
payload['language_code'] = language_code
return await _process_request(token, method_url, params=payload)
+
+async def set_my_name(token, name=None, language_code=None):
+ method_url = r'setMyName'
+ payload = {}
+ if name is not None:
+ payload['name'] = name
+ if language_code is not None:
+ payload['language_code'] = language_code
+ return await _process_request(token, method_url, params=payload, method='post')
+
+async def get_my_name(token, language_code=None):
+ method_url = r'getMyName'
+ payload = {}
+ if language_code is not None:
+ payload['language_code'] = language_code
+ return await _process_request(token, method_url, params=payload)
+
async def set_chat_menu_button(token, chat_id=None, menu_button=None):
method_url = r'setChatMenuButton'
payload = {}
@@ -1587,7 +1604,7 @@ async def answer_callback_query(token, callback_query_id, text=None, show_alert=
async def answer_inline_query(token, inline_query_id, results, cache_time=None, is_personal=None, next_offset=None,
- switch_pm_text=None, switch_pm_parameter=None):
+ button=None):
method_url = 'answerInlineQuery'
payload = {'inline_query_id': inline_query_id, 'results': await _convert_list_json_serializable(results)}
if cache_time is not None:
@@ -1596,10 +1613,10 @@ async def answer_inline_query(token, inline_query_id, results, cache_time=None,
payload['is_personal'] = is_personal
if next_offset is not None:
payload['next_offset'] = next_offset
- if switch_pm_text:
- payload['switch_pm_text'] = switch_pm_text
- if switch_pm_parameter:
- payload['switch_pm_parameter'] = switch_pm_parameter
+ if button is not None:
+ payload["button"] = button.to_json()
+
+
return await _process_request(token, method_url, params=payload, method='post')
diff --git a/telebot/types.py b/telebot/types.py
index 22d46c9..b793073 100644
--- a/telebot/types.py
+++ b/telebot/types.py
@@ -237,6 +237,9 @@ class ChatMemberUpdated(JsonDeserializable):
link events only.
:type invite_link: :class:`telebot.types.ChatInviteLink`
+ :param via_chat_folder_invite_link: Optional. True, if the user joined the chat via a chat folder invite link
+ :type via_chat_folder_invite_link: :obj:`bool`
+
:return: Instance of the class
:rtype: :class:`telebot.types.ChatMemberUpdated`
"""
@@ -251,13 +254,15 @@ class ChatMemberUpdated(JsonDeserializable):
obj['invite_link'] = ChatInviteLink.de_json(obj.get('invite_link'))
return cls(**obj)
- def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invite_link=None, **kwargs):
+ def __init__(self, chat, from_user, date, old_chat_member, new_chat_member, invite_link=None, via_chat_folder_invite_link=None,
+ **kwargs):
self.chat: Chat = chat
self.from_user: User = from_user
self.date: int = date
self.old_chat_member: ChatMember = old_chat_member
self.new_chat_member: ChatMember = new_chat_member
self.invite_link: Optional[ChatInviteLink] = invite_link
+ self.via_chat_folder_invite_link: Optional[bool] = via_chat_folder_invite_link
@property
def difference(self) -> Dict[str, List]:
@@ -1308,6 +1313,7 @@ class Message(JsonDeserializable):
"strikethrough": "{text}",
"underline": "{text}",
"spoiler": "{text}",
+ "custom_emoji": "{text}"
}
if hasattr(self, "custom_subs"):
@@ -1316,7 +1322,7 @@ class Message(JsonDeserializable):
utf16_text = text.encode("utf-16-le")
html_text = ""
- def func(upd_text, subst_type=None, url=None, user=None):
+ def func(upd_text, subst_type=None, url=None, user=None, custom_emoji_id=None):
upd_text = upd_text.decode("utf-16-le")
if subst_type == "text_mention":
subst_type = "text_link"
@@ -1327,30 +1333,41 @@ class Message(JsonDeserializable):
if not subst_type or not _subs.get(subst_type):
return upd_text
subs = _subs.get(subst_type)
+ if subst_type == "custom_emoji":
+ return subs.format(text=upd_text, custom_emoji_id=custom_emoji_id)
return subs.format(text=upd_text, url=url)
offset = 0
+ start_index = 0
+ end_index = 0
for entity in entities:
if entity.offset > offset:
+ # when the offset is not 0: for example, a __b__
+ # we need to add the text before the entity to the html_text
html_text += func(utf16_text[offset * 2 : entity.offset * 2])
offset = entity.offset
- html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user)
+
+ new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id)
+ start_index = len(html_text)
+ html_text += new_string
offset += entity.length
+ end_index = len(html_text)
elif entity.offset == offset:
- html_text += func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user)
+ new_string = func(utf16_text[offset * 2 : (offset + entity.length) * 2], entity.type, entity.url, entity.user, entity.custom_emoji_id)
+ start_index = len(html_text)
+ html_text += new_string
+ end_index = len(html_text)
offset += entity.length
else:
# Here we are processing nested entities.
# We shouldn't update offset, because they are the same as entity before.
# And, here we are replacing previous string with a new html-rendered text(previous string is already html-rendered,
# And we don't change it).
- entity_string = utf16_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
- formatted_string = func(entity_string, entity.type, entity.url, entity.user)
- entity_string_decoded = entity_string.decode("utf-16-le")
- last_occurence = html_text.rfind(entity_string_decoded)
- string_length = len(entity_string_decoded)
- #html_text = html_text.replace(html_text[last_occurence:last_occurence+string_length], formatted_string)
- html_text = html_text[:last_occurence] + formatted_string + html_text[last_occurence+string_length:]
+ entity_string = html_text[start_index : end_index].encode("utf-16-le")
+ formatted_string = func(entity_string, entity.type, entity.url, entity.user, entity.custom_emoji_id).replace("&", "&").replace("<", "<").replace(">",">")
+ html_text = html_text[:start_index] + formatted_string + html_text[end_index:]
+ end_index = len(html_text)
+
if offset * 2 < len(utf16_text):
html_text += func(utf16_text[offset * 2:])
@@ -2592,6 +2609,10 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable)
something from multiple options.
:type switch_inline_query_current_chat: :obj:`str`
+ :param switch_inline_query_chosen_chat: Optional. If set, pressing the button will prompt the user to select one of their chats of the
+ specified type, open that chat and insert the bot's username and the specified inline query in the input field
+ :type switch_inline_query_chosen_chat: :class:`telebot.types.SwitchInlineQueryChosenChat`
+
:param callback_game: Optional. Description of the game that will be launched when the user presses the
button. NOTE: This type of button must always be the first button in the first row.
:type callback_game: :class:`telebot.types.CallbackGame`
@@ -2611,17 +2632,20 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable)
obj['login_url'] = LoginUrl.de_json(obj.get('login_url'))
if 'web_app' in obj:
obj['web_app'] = WebAppInfo.de_json(obj.get('web_app'))
+ if 'switch_inline_query_chosen_chat' in obj:
+ obj['switch_inline_query_chosen_chat'] = SwitchInlineQueryChosenChat.de_json(obj.get('switch_inline_query_chosen_chat'))
return cls(**obj)
def __init__(self, text, url=None, callback_data=None, web_app=None, switch_inline_query=None,
- switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs):
+ switch_inline_query_current_chat=None, switch_inline_query_chosen_chat=None, callback_game=None, pay=None, login_url=None, **kwargs):
self.text: str = text
self.url: str = url
self.callback_data: str = callback_data
self.web_app: WebAppInfo = web_app
self.switch_inline_query: str = switch_inline_query
self.switch_inline_query_current_chat: str = switch_inline_query_current_chat
+ self.switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat = switch_inline_query_chosen_chat
self.callback_game = callback_game # Not Implemented
self.pay: bool = pay
self.login_url: LoginUrl = login_url
@@ -2647,6 +2671,8 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable)
json_dict['pay'] = self.pay
if self.login_url is not None:
json_dict['login_url'] = self.login_url.to_dict()
+ if self.switch_inline_query_chosen_chat is not None:
+ json_dict['switch_inline_query_chosen_chat'] = self.switch_inline_query_chosen_chat.to_dict()
return json_dict
@@ -7396,13 +7422,20 @@ class WriteAccessAllowed(JsonDeserializable):
Currently holds no information.
Telegram documentation: https://core.telegram.org/bots/api#writeaccessallowed
+
+ :param web_app_name: Optional. Name of the Web App which was launched from a link
+ :type web_app_name: :obj:`str`
"""
@classmethod
def de_json(cls, json_string):
- return cls()
+ if json_string is None: return None
+ obj = cls.check_json(json_string)
+ return cls(**obj)
+
- def __init__(self) -> None:
- pass
+ def __init__(self, web_app_name: str) -> None:
+ self.web_app_name: str = web_app_name
+
class UserShared(JsonDeserializable):
@@ -7576,4 +7609,137 @@ class InputSticker(Dictionaryable, JsonSerializable):
return self.to_json(), {self._sticker_name: self.sticker}
-
\ No newline at end of file
+
+class SwitchInlineQueryChosenChat(JsonDeserializable, Dictionaryable, JsonSerializable):
+ """
+ Represents an inline button that switches the current user to inline mode in a chosen chat,
+ with an optional default inline query.
+
+ Telegram Documentation: https://core.telegram.org/bots/api#inlinekeyboardbutton
+
+ :param query: Optional. The default inline query to be inserted in the input field.
+ If left empty, only the bot's username will be inserted
+ :type query: :obj:`str`
+
+ :param allow_user_chats: Optional. True, if private chats with users can be chosen
+ :type allow_user_chats: :obj:`bool`
+
+ :param allow_bot_chats: Optional. True, if private chats with bots can be chosen
+ :type allow_bot_chats: :obj:`bool`
+
+ :param allow_group_chats: Optional. True, if group and supergroup chats can be chosen
+ :type allow_group_chats: :obj:`bool`
+
+ :param allow_channel_chats: Optional. True, if channel chats can be chosen
+ :type allow_channel_chats: :obj:`bool`
+
+ :return: Instance of the class
+ :rtype: :class:`SwitchInlineQueryChosenChat`
+ """
+
+ @classmethod
+ def de_json(cls, json_string):
+ if json_string is None:
+ return None
+ obj = cls.check_json(json_string)
+ return cls(**obj)
+
+ def __init__(self, query=None, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None,
+ allow_channel_chats=None):
+ self.query: str = query
+ self.allow_user_chats: bool = allow_user_chats
+ self.allow_bot_chats: bool = allow_bot_chats
+ self.allow_group_chats: bool = allow_group_chats
+ self.allow_channel_chats: bool = allow_channel_chats
+
+ def to_dict(self):
+ json_dict = {}
+
+ if self.query is not None:
+ json_dict['query'] = self.query
+ if self.allow_user_chats is not None:
+ json_dict['allow_user_chats'] = self.allow_user_chats
+ if self.allow_bot_chats is not None:
+ json_dict['allow_bot_chats'] = self.allow_bot_chats
+ if self.allow_group_chats is not None:
+ json_dict['allow_group_chats'] = self.allow_group_chats
+ if self.allow_channel_chats is not None:
+ json_dict['allow_channel_chats'] = self.allow_channel_chats
+
+ return json_dict
+
+ def to_json(self):
+ return json.dumps(self.to_dict())
+
+
+class BotName(JsonDeserializable):
+ """
+ This object represents a bot name.
+
+ Telegram Documentation: https://core.telegram.org/bots/api#botname
+
+ :param name: The bot name
+ :type name: :obj:`str`
+
+ :return: Instance of the class
+ :rtype: :class:`BotName`
+ """
+
+ @classmethod
+ def de_json(cls, json_string):
+ if json_string is None:
+ return None
+ obj = cls.check_json(json_string)
+ return cls(**obj)
+
+ def __init__(self, name: str):
+ self.name: str = name
+
+
+class InlineQueryResultsButton(JsonSerializable, Dictionaryable):
+ """
+ This object represents a button to be shown above inline query results.
+ You must use exactly one of the optional fields.
+
+ Telegram documentation: https://core.telegram.org/bots/api#inlinequeryresultsbutton
+
+ :param text: Label text on the button
+ :type text: :obj:`str`
+
+ :param web_app: Optional. Description of the Web App that will be launched when the user presses the button.
+ The Web App will be able to switch back to the inline mode using the method web_app_switch_inline_query inside the Web App.
+ :type web_app: :class:`telebot.types.WebAppInfo`
+
+ :param start_parameter: Optional. Deep-linking parameter for the /start message sent to the bot when a user presses the button.
+ 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
+ Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search
+ results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing
+ any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs
+ the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat
+ where they wanted to use the bot's inline capabilities.
+ :type start_parameter: :obj:`str`
+
+ :return: Instance of the class
+ :rtype: :class:`InlineQueryResultsButton`
+ """
+
+ def __init__(self, text: str, web_app: Optional[WebAppInfo]=None, start_parameter: Optional[str]=None) -> None:
+ self.text: str = text
+ self.web_app: Optional[WebAppInfo] = web_app
+ self.start_parameter: Optional[str] = start_parameter
+
+
+ def to_dict(self) -> dict:
+ json_dict = {
+ 'text': self.text
+ }
+
+ if self.web_app is not None:
+ json_dict['web_app'] = self.web_app.to_dict()
+ if self.start_parameter is not None:
+ json_dict['start_parameter'] = self.start_parameter
+
+ return json_dict
+
+ def to_json(self) -> str:
+ return json.dumps(self.to_dict())
\ No newline at end of file
diff --git a/tests/test_types.py b/tests/test_types.py
index c696f16..138c36b 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -271,5 +271,33 @@ def test_sent_web_app_message():
assert sent_web_app_message.inline_message_id == '29430'
+def test_message_entity():
+ # TODO: Add support for nesting entities
+
+
+ sample_string_1 = r'{"update_id":934522126,"message":{"message_id":1374510,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682177590,"text":"b b b","entities":[{"offset":0,"length":2,"type":"bold"},{"offset":0,"length":1,"type":"italic"},{"offset":2,"length":2,"type":"bold"},{"offset":2,"length":1,"type":"italic"},{"offset":4,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}'
+ update = types.Update.de_json(sample_string_1)
+ message: types.Message = update.message
+ assert message.html_text == "b b b"
+
+ sample_string_2 = r'{"update_id":934522166,"message":{"message_id":1374526,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179716,"text":"b b b","entities":[{"offset":0,"length":1,"type":"bold"},{"offset":2,"length":1,"type":"bold"},{"offset":4,"length":1,"type":"italic"}]}}'
+ message_2 = types.Update.de_json(sample_string_2).message
+ assert message_2.html_text == "b b b"
+
+
+
+ sample_string_3 = r'{"update_id":934522172,"message":{"message_id":1374530,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682179968,"text":"This is a bold text with a nested italic and bold text.","entities":[{"offset":10,"length":4,"type":"bold"},{"offset":27,"length":7,"type":"italic"},{"offset":34,"length":15,"type":"bold"},{"offset":34,"length":15,"type":"italic"}]}}'
+ message_3 = types.Update.de_json(sample_string_3).message
+ assert message_3.html_text == "This is a bold text with a nested italic and bold text."
+
+
+ sample_string_4 = r'{"update_id":934522437,"message":{"message_id":1374619,"from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"chat":{"id":927266710,"first_name":">_run","username":"coder2020","type":"private"},"date":1682189507,"forward_from":{"id":927266710,"is_bot":false,"first_name":">_run","username":"coder2020","language_code":"en","is_premium":true},"forward_date":1682189124,"text":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa😋😋","entities":[{"offset":0,"length":76,"type":"bold"},{"offset":0,"length":76,"type":"italic"},{"offset":0,"length":76,"type":"underline"},{"offset":0,"length":76,"type":"strikethrough"},{"offset":76,"length":2,"type":"custom_emoji","custom_emoji_id":"5456188142006575553"},{"offset":78,"length":2,"type":"custom_emoji","custom_emoji_id":"5456188142006575553"}]}}'
+ message_4 = types.Update.de_json(sample_string_4).message
+ assert message_4.html_text == 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa😋😋'
+
+
+
+
+