Full support for Bot API 9.5 (#5155)

Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: OuYoung <212045739+ouyooung@users.noreply.github.com>
Co-authored-by: Hethon <65696516+hethon@users.noreply.github.com>
Co-authored-by: Abdelrahman Elkheir <90580077+aelkheir@users.noreply.github.com>
This commit is contained in:
Poolitzer
2026-03-15 07:58:30 +01:00
committed by GitHub
parent fb5234d9f5
commit 0cceafcab3
22 changed files with 569 additions and 24 deletions
+15
View File
@@ -2835,6 +2835,18 @@ class TestBotWithoutRequest:
limit="limit",
)
# TODO if we have a group member id in every group we could test this
async def test_set_chat_member_tag(self, offline_bot, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
assert request_data.parameters.get("chat_id") == 1234
assert request_data.parameters.get("user_id") == 5678
assert request_data.parameters.get("tag") == "This is a tag"
monkeypatch.setattr(offline_bot.request, "post", make_assertion)
await offline_bot.set_chat_member_tag(1234, 5678, "This is a tag")
class TestBotWithRequest:
"""
@@ -3808,6 +3820,7 @@ class TestBotWithRequest:
can_edit_stories=True,
can_delete_stories=True,
can_manage_direct_messages=True,
can_manage_tags=True,
)
# Test that we pass the correct params to TG
@@ -3832,6 +3845,7 @@ class TestBotWithRequest:
and data.get("can_edit_stories") == 14
and data.get("can_delete_stories") == 15
and data.get("can_manage_direct_messages") == 16
and data.get("can_manage_tags") == 17
)
monkeypatch.setattr(bot, "_post", make_assertion)
@@ -3854,6 +3868,7 @@ class TestBotWithRequest:
can_edit_stories=14,
can_delete_stories=15,
can_manage_direct_messages=16,
can_manage_tags=17,
)
async def test_export_chat_invite_link(self, bot, channel_id):
+19
View File
@@ -1548,6 +1548,25 @@ class TestChatWithoutRequest(ChatTestBase):
monkeypatch.setattr(chat.get_bot(), "get_chat_gifts", make_assertion)
assert await chat.get_gifts()
async def test_instance_method_set_chat_member_tag(self, monkeypatch, chat):
async def make_assertion(*_, **kwargs):
return (
kwargs["chat_id"] == chat.id
and kwargs["user_id"] == "user_id"
and kwargs["tag"] == "tag"
)
assert check_shortcut_signature(
Chat.set_chat_member_tag, Bot.set_chat_member_tag, ["chat_id"], []
)
assert await check_shortcut_call(
chat.set_chat_member_tag, chat.get_bot(), "set_chat_member_tag"
)
assert await check_defaults_handling(chat.set_chat_member_tag, chat.get_bot())
monkeypatch.setattr(chat.get_bot(), "set_chat_member_tag", make_assertion)
assert await chat.set_chat_member_tag(user_id="user_id", tag="tag")
def test_mention_html(self):
chat = Chat(id=1, type="foo")
with pytest.raises(TypeError, match="Can not create a mention to a private group chat"):
+5
View File
@@ -41,6 +41,7 @@ def chat_admin_rights():
can_edit_stories=True,
can_delete_stories=True,
can_manage_direct_messages=True,
can_manage_tags=True,
)
@@ -69,6 +70,7 @@ class TestChatAdministratorRightsWithoutRequest:
"can_edit_stories": True,
"can_delete_stories": True,
"can_manage_direct_messages": True,
"can_manage_tags": True,
}
chat_administrator_rights_de = ChatAdministratorRights.de_json(json_dict, offline_bot)
assert chat_administrator_rights_de.api_kwargs == {}
@@ -96,6 +98,7 @@ class TestChatAdministratorRightsWithoutRequest:
assert admin_rights_dict["can_edit_stories"] == car.can_edit_stories
assert admin_rights_dict["can_delete_stories"] == car.can_delete_stories
assert admin_rights_dict["can_manage_direct_messages"] == car.can_manage_direct_messages
assert admin_rights_dict["can_manage_tags"] == car.can_manage_tags
def test_equality(self):
a = ChatAdministratorRights(
@@ -147,6 +150,7 @@ class TestChatAdministratorRightsWithoutRequest:
True,
True,
True,
True,
)
t = ChatAdministratorRights.all_rights()
# if the dirs are the same, the attributes will all be there
@@ -173,6 +177,7 @@ class TestChatAdministratorRightsWithoutRequest:
False,
False,
False,
False,
)
t = ChatAdministratorRights.no_rights()
# if the dirs are the same, the attributes will all be there
+19
View File
@@ -76,6 +76,9 @@ class ChatMemberTestBase:
can_send_messages = True
is_member = True
can_manage_direct_messages = True
can_manage_tags = True
can_edit_tag = True
tag = "test_tag"
class TestChatMemberWithoutRequest(ChatMemberTestBase):
@@ -172,6 +175,7 @@ def chat_member_administrator():
TestChatMemberAdministratorWithoutRequest.custom_title,
TestChatMemberAdministratorWithoutRequest.is_anonymous,
TestChatMemberAdministratorWithoutRequest.can_manage_direct_messages,
TestChatMemberAdministratorWithoutRequest.can_manage_tags,
)
@@ -205,6 +209,7 @@ class TestChatMemberAdministratorWithoutRequest(ChatMemberTestBase):
"custom_title": self.custom_title,
"is_anonymous": self.is_anonymous,
"can_manage_direct_messages": self.can_manage_direct_messages,
"can_manage_tags": self.can_manage_tags,
}
chat_member = ChatMemberAdministrator.de_json(data, offline_bot)
@@ -230,6 +235,7 @@ class TestChatMemberAdministratorWithoutRequest(ChatMemberTestBase):
assert chat_member.custom_title == self.custom_title
assert chat_member.is_anonymous == self.is_anonymous
assert chat_member.can_manage_direct_messages == self.can_manage_direct_messages
assert chat_member.can_manage_tags == self.can_manage_tags
def test_to_dict(self, chat_member_administrator):
assert chat_member_administrator.to_dict() == {
@@ -253,6 +259,7 @@ class TestChatMemberAdministratorWithoutRequest(ChatMemberTestBase):
"custom_title": chat_member_administrator.custom_title,
"is_anonymous": chat_member_administrator.is_anonymous,
"can_manage_direct_messages": chat_member_administrator.can_manage_direct_messages,
"can_manage_tags": chat_member_administrator.can_manage_tags,
}
def test_equality(self, chat_member_administrator):
@@ -272,6 +279,7 @@ class TestChatMemberAdministratorWithoutRequest(ChatMemberTestBase):
True,
True,
True,
True,
)
c = ChatMemberAdministrator(
User(1, "test_user", is_bot=False),
@@ -288,6 +296,7 @@ class TestChatMemberAdministratorWithoutRequest(ChatMemberTestBase):
False,
False,
False,
False,
)
d = Dice(5, "test")
@@ -566,6 +575,8 @@ def chat_member_restricted():
can_send_voice_notes=TestChatMemberRestrictedWithoutRequest.can_send_voice_notes,
is_member=TestChatMemberRestrictedWithoutRequest.is_member,
until_date=TestChatMemberRestrictedWithoutRequest.until_date,
can_edit_tag=TestChatMemberRestrictedWithoutRequest.can_edit_tag,
tag=TestChatMemberRestrictedWithoutRequest.tag,
)
@@ -597,6 +608,8 @@ class TestChatMemberRestrictedWithoutRequest(ChatMemberTestBase):
"can_send_voice_notes": self.can_send_voice_notes,
"is_member": self.is_member,
"until_date": to_timestamp(self.until_date),
"can_edit_tag": self.can_edit_tag,
"tag": self.tag,
# legacy argument
"can_send_media_messages": False,
}
@@ -622,6 +635,8 @@ class TestChatMemberRestrictedWithoutRequest(ChatMemberTestBase):
assert chat_member.can_send_voice_notes == self.can_send_voice_notes
assert chat_member.is_member == self.is_member
assert chat_member.until_date == self.until_date
assert chat_member.can_edit_tag == self.can_edit_tag
assert chat_member.tag == self.tag
def test_de_json_localization(self, tz_bot, offline_bot, raw_bot, chat_member_restricted):
json_dict = chat_member_restricted.to_dict()
@@ -660,6 +675,8 @@ class TestChatMemberRestrictedWithoutRequest(ChatMemberTestBase):
"can_send_voice_notes": chat_member_restricted.can_send_voice_notes,
"is_member": chat_member_restricted.is_member,
"until_date": to_timestamp(chat_member_restricted.until_date),
"can_edit_tag": chat_member_restricted.can_edit_tag,
"tag": chat_member_restricted.tag,
}
def test_equality(self, chat_member_restricted):
@@ -683,6 +700,8 @@ class TestChatMemberRestrictedWithoutRequest(ChatMemberTestBase):
False,
False,
False,
False,
"tag",
)
d = Dice(5, "test")
+7
View File
@@ -40,6 +40,7 @@ def chat_permissions():
can_send_videos=True,
can_send_video_notes=True,
can_send_voice_notes=True,
can_edit_tag=True,
)
@@ -58,6 +59,7 @@ class ChatPermissionsTestBase:
can_send_videos = True
can_send_video_notes = False
can_send_voice_notes = None
can_edit_tag = None
class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
@@ -83,6 +85,7 @@ class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
"can_send_videos": self.can_send_videos,
"can_send_video_notes": self.can_send_video_notes,
"can_send_voice_notes": self.can_send_voice_notes,
"can_edit_tag": self.can_edit_tag,
}
permissions = ChatPermissions.de_json(json_dict, offline_bot)
assert permissions.api_kwargs == {"can_send_media_messages": "can_send_media_messages"}
@@ -101,6 +104,7 @@ class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
assert permissions.can_send_videos == self.can_send_videos
assert permissions.can_send_video_notes == self.can_send_video_notes
assert permissions.can_send_voice_notes == self.can_send_voice_notes
assert permissions.can_edit_tag == self.can_edit_tag
def test_to_dict(self, chat_permissions):
permissions_dict = chat_permissions.to_dict()
@@ -125,6 +129,7 @@ class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
assert permissions_dict["can_send_videos"] == chat_permissions.can_send_videos
assert permissions_dict["can_send_video_notes"] == chat_permissions.can_send_video_notes
assert permissions_dict["can_send_voice_notes"] == chat_permissions.can_send_voice_notes
assert permissions_dict["can_edit_tag"] == chat_permissions.can_edit_tag
def test_equality(self):
a = ChatPermissions(
@@ -153,6 +158,7 @@ class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
can_send_videos=True,
can_send_video_notes=True,
can_send_voice_notes=True,
can_edit_tag=True,
)
f = ChatPermissions(
can_send_messages=True,
@@ -164,6 +170,7 @@ class TestChatPermissionsWithoutRequest(ChatPermissionsTestBase):
can_send_videos=True,
can_send_video_notes=True,
can_send_voice_notes=True,
can_edit_tag=True,
)
assert a == b
+1
View File
@@ -198,6 +198,7 @@ class TestConstantsWithoutRequest:
"reply_markup",
"reply_to_message",
"sender_chat",
"sender_tag",
"is_accessible",
"quote",
"external_reply",
+38 -6
View File
@@ -19,6 +19,7 @@
import datetime as dtm
from copy import copy, deepcopy
from zoneinfo import ZoneInfo
import pytest
@@ -434,6 +435,7 @@ def message(bot):
},
{"chat_owner_changed": ChatOwnerChanged(new_owner=User(4, "Snow", False))},
{"chat_owner_left": ChatOwnerLeft(new_owner=User(5, "Crash", False))},
{"sender_tag": "This is a tag"},
],
ids=[
"reply",
@@ -527,6 +529,7 @@ def message(bot):
"gift_upgrade_sent",
"chat_owner_changed",
"chat_owner_left",
"sender_tag",
],
)
def message_params(bot, request):
@@ -585,11 +588,20 @@ class MessageTestBase:
{"length": 34, "offset": 154, "type": "blockquote"},
{"length": 6, "offset": 181, "type": "bold"},
{"length": 33, "offset": 190, "type": "expandable_blockquote"},
{"length": 4, "offset": 224, "type": "date_time", "unix_time": dtm.datetime(2000, 7, 28)},
{
"length": 14,
"offset": 229,
"type": "date_time",
"unix_time": dtm.datetime(2000, 7, 28, tzinfo=ZoneInfo("Europe/Berlin")),
"date_time_format": "r",
},
]
test_text_v2 = (
r"Test for <bold, ita_lic, \`code, links, text-mention and `\pre. "
"http://google.com and bold nested in strk>trgh nested in italic. Python pre. Spoiled. "
"👍.\nMultiline\nblock quote\nwith nested.\n\nMultiline\nexpandable\nblock quote."
"👍.\nMultiline\nblock quote\nwith nested.\n\nMultiline\nexpandable\nblock quote.\ntime"
"\ntime_formatted\n"
)
test_message = Message(
message_id=1,
@@ -957,7 +969,9 @@ class TestMessageWithoutRequest(MessageTestBase):
'<span class="tg-spoiler">Spoiled</span>. '
'<tg-emoji emoji-id="1">👍</tg-emoji>.\n'
"<blockquote>Multiline\nblock quote\nwith <b>nested</b>.</blockquote>\n\n"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>\n"
'<tg-time unix="964742400">time</tg-time>\n'
'<tg-time unix="964735200" format="r">time_formatted</tg-time>\n'
)
text_html = self.test_message_v2.text_html
assert text_html == test_html_string
@@ -979,7 +993,9 @@ class TestMessageWithoutRequest(MessageTestBase):
'<span class="tg-spoiler">Spoiled</span>. '
'<tg-emoji emoji-id="1">👍</tg-emoji>.\n'
"<blockquote>Multiline\nblock quote\nwith <b>nested</b>.</blockquote>\n\n"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>\n"
'<tg-time unix="964742400">time</tg-time>\n'
'<tg-time unix="964735200" format="r">time_formatted</tg-time>\n'
)
text_html = self.test_message_v2.text_html_urled
assert text_html == test_html_string
@@ -1007,6 +1023,8 @@ class TestMessageWithoutRequest(MessageTestBase):
"\n\n>Multiline\n"
">expandable\n"
r">block quote\.||"
"\n![time](tg://time?unix=964742400)\n"
"![time\\_formatted](tg://time?unix=964735200&format=r)\n"
)
text_markdown = self.test_message_v2.text_markdown_v2
assert text_markdown == test_md_string
@@ -1066,6 +1084,8 @@ class TestMessageWithoutRequest(MessageTestBase):
"\n\n>Multiline\n"
">expandable\n"
r">block quote\.||"
"\n![time](tg://time?unix=964742400)\n"
"![time\\_formatted](tg://time?unix=964735200&format=r)\n"
)
text_markdown = self.test_message_v2.text_markdown_v2_urled
assert text_markdown == test_md_string
@@ -1183,7 +1203,9 @@ class TestMessageWithoutRequest(MessageTestBase):
'<span class="tg-spoiler">Spoiled</span>. '
'<tg-emoji emoji-id="1">👍</tg-emoji>.\n'
"<blockquote>Multiline\nblock quote\nwith <b>nested</b>.</blockquote>\n\n"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>\n"
'<tg-time unix="964742400">time</tg-time>\n'
'<tg-time unix="964735200" format="r">time_formatted</tg-time>\n'
)
caption_html = self.test_message_v2.caption_html
assert caption_html == test_html_string
@@ -1205,7 +1227,9 @@ class TestMessageWithoutRequest(MessageTestBase):
'<span class="tg-spoiler">Spoiled</span>. '
'<tg-emoji emoji-id="1">👍</tg-emoji>.\n'
"<blockquote>Multiline\nblock quote\nwith <b>nested</b>.</blockquote>\n\n"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>\n"
'<tg-time unix="964742400">time</tg-time>\n'
'<tg-time unix="964735200" format="r">time_formatted</tg-time>\n'
)
caption_html = self.test_message_v2.caption_html_urled
assert caption_html == test_html_string
@@ -1233,6 +1257,8 @@ class TestMessageWithoutRequest(MessageTestBase):
"\n\n>Multiline\n"
">expandable\n"
r">block quote\.||"
"\n![time](tg://time?unix=964742400)\n"
"![time\\_formatted](tg://time?unix=964735200&format=r)\n"
)
caption_markdown = self.test_message_v2.caption_markdown_v2
assert caption_markdown == test_md_string
@@ -1267,6 +1293,8 @@ class TestMessageWithoutRequest(MessageTestBase):
"\n\n>Multiline\n"
">expandable\n"
r">block quote\.||"
"\n![time](tg://time?unix=964742400)\n"
"![time\\_formatted](tg://time?unix=964735200&format=r)\n"
)
caption_markdown = self.test_message_v2.caption_markdown_v2_urled
assert caption_markdown == test_md_string
@@ -1746,6 +1774,8 @@ class TestMessageWithoutRequest(MessageTestBase):
"\n\n>Multiline\n"
">expandable\n"
r">block quote\.||"
"\n![time](tg://time?unix=964742400)\n"
"![time\\_formatted](tg://time?unix=964735200&format=r)\n"
)
async def make_assertion(*_, **kwargs):
@@ -1803,7 +1833,9 @@ class TestMessageWithoutRequest(MessageTestBase):
'<span class="tg-spoiler">Spoiled</span>. '
'<tg-emoji emoji-id="1">👍</tg-emoji>.\n'
"<blockquote>Multiline\nblock quote\nwith <b>nested</b>.</blockquote>\n\n"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>"
"<blockquote expandable>Multiline\nexpandable\nblock quote.</blockquote>\n"
'<tg-time unix="964742400">time</tg-time>\n'
'<tg-time unix="964735200" format="r">time_formatted</tg-time>\n'
)
async def make_assertion(*_, **kwargs):
+26 -1
View File
@@ -16,11 +16,13 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime as dtm
import random
import pytest
from telegram import MessageEntity, User
from telegram._utils.datetime import UTC, to_timestamp
from telegram.constants import MessageEntityType
from tests.auxil.slots import mro_slots
@@ -37,7 +39,25 @@ def message_entity(request):
language = None
if type_ == MessageEntity.PRE:
language = "python"
return MessageEntity(type_, 1, 3, url=url, user=user, language=language)
custom_emoji_id = None
if type_ == MessageEntity.CUSTOM_EMOJI:
custom_emoji_id = "emoji_id"
date_time_format = None
unix_time = None
if type_ == MessageEntity.DATE_TIME:
date_time_format = "wDT"
unix_time = dtm.datetime.now(tz=UTC)
return MessageEntity(
type_,
1,
3,
url=url,
user=user,
language=language,
custom_emoji_id=custom_emoji_id,
date_time_format=date_time_format,
unix_time=unix_time,
)
class MessageEntityTestBase:
@@ -76,6 +96,11 @@ class TestMessageEntityWithoutRequest(MessageEntityTestBase):
assert entity_dict["user"] == message_entity.user.to_dict()
if message_entity.language:
assert entity_dict["language"] == message_entity.language
if message_entity.custom_emoji_id:
assert entity_dict["custom_emoji_id"] == message_entity.custom_emoji_id
if message_entity.date_time_format:
assert entity_dict["date_time_format"] == message_entity.date_time_format
assert entity_dict["unix_time"] == to_timestamp(message_entity.unix_time)
def test_enum_init(self):
entity = MessageEntity(type="foo", offset=0, length=1)
+19
View File
@@ -887,3 +887,22 @@ class TestUserWithoutRequest(UserTestBase):
monkeypatch.setattr(user.get_bot(), "get_user_gifts", make_assertion)
assert await user.get_gifts()
async def test_instance_method_set_chat_member_tag(self, monkeypatch, user):
async def make_assertion(*_, **kwargs):
return (
kwargs["user_id"] == user.id
and kwargs["chat_id"] == "chat_id"
and kwargs["tag"] == "tag"
)
assert check_shortcut_signature(
user.set_chat_member_tag, Bot.set_chat_member_tag, ["user_id"], []
)
assert await check_shortcut_call(
user.set_chat_member_tag, user.get_bot(), "set_chat_member_tag"
)
assert await check_defaults_handling(user.set_chat_member_tag, user.get_bot())
monkeypatch.setattr(user.get_bot(), "set_chat_member_tag", make_assertion)
assert await user.set_chat_member_tag(chat_id="chat_id", tag="tag")