#!/usr/bin/env python # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2024 # Leandro Toledo de Souza # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser Public License for more details. # # 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 contextlib from copy import copy from datetime import datetime import pytest from telegram import ( Animation, Audio, BackgroundTypeChatTheme, Bot, Chat, ChatBackground, ChatBoostAdded, ChatShared, Contact, Dice, Document, ExternalReplyInfo, Game, Giveaway, GiveawayCompleted, GiveawayCreated, GiveawayWinners, InputPaidMediaPhoto, Invoice, LinkPreviewOptions, Location, Message, MessageAutoDeleteTimerChanged, MessageEntity, MessageOriginChat, PaidMediaInfo, PaidMediaPreview, PassportData, PhotoSize, Poll, PollOption, ProximityAlertTriggered, RefundedPayment, ReplyParameters, SharedUser, Sticker, Story, SuccessfulPayment, TextQuote, Update, User, UsersShared, Venue, Video, VideoChatEnded, VideoChatParticipantsInvited, VideoChatScheduled, VideoChatStarted, VideoNote, Voice, WebAppData, ) from telegram._utils.datetime import UTC from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import ODVInput from telegram.constants import ChatAction, ParseMode from telegram.ext import Defaults from telegram.warnings import PTBDeprecationWarning from tests._passport.test_passport import RAW_PASSPORT_DATA from tests.auxil.bot_method_checks import ( check_defaults_handling, check_shortcut_call, check_shortcut_signature, ) from tests.auxil.build_messages import make_message from tests.auxil.pytest_classes import PytestExtBot, PytestMessage from tests.auxil.slots import mro_slots @pytest.fixture def message(bot): message = PytestMessage( message_id=MessageTestBase.id_, date=MessageTestBase.date, chat=copy(MessageTestBase.chat), from_user=copy(MessageTestBase.from_user), business_connection_id="123456789", ) message.set_bot(bot) message._unfreeze() message.chat._unfreeze() message.from_user._unfreeze() return message @pytest.fixture( params=[ { "reply_to_message": Message( 50, datetime.utcnow(), Chat(13, "channel"), User(9, "i", False) ) }, {"edit_date": datetime.utcnow()}, { "text": "a text message", "entities": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], }, { "caption": "A message caption", "caption_entities": [MessageEntity("bold", 1, 1), MessageEntity("text_link", 4, 3)], }, {"audio": Audio("audio_id", "unique_id", 12), "caption": "audio_file"}, {"document": Document("document_id", "unique_id"), "caption": "document_file"}, { "animation": Animation("animation_id", "unique_id", 30, 30, 1), "caption": "animation_file", }, { "game": Game( "my_game", "just my game", [ PhotoSize("game_photo_id", "unique_id", 30, 30), ], ) }, {"photo": [PhotoSize("photo_id", "unique_id", 50, 50)], "caption": "photo_file"}, {"sticker": Sticker("sticker_id", "unique_id", 50, 50, True, False, Sticker.REGULAR)}, {"story": Story(Chat(1, Chat.PRIVATE), 0)}, {"video": Video("video_id", "unique_id", 12, 12, 12), "caption": "video_file"}, {"voice": Voice("voice_id", "unique_id", 5)}, {"video_note": VideoNote("video_note_id", "unique_id", 20, 12)}, {"new_chat_members": [User(55, "new_user", False)]}, {"contact": Contact("phone_numner", "contact_name")}, {"location": Location(-23.691288, 46.788279)}, {"venue": Venue(Location(-23.691288, 46.788279), "some place", "right here")}, {"left_chat_member": User(33, "kicked", False)}, {"new_chat_title": "new title"}, {"new_chat_photo": [PhotoSize("photo_id", "unique_id", 50, 50)]}, {"delete_chat_photo": True}, {"group_chat_created": True}, {"supergroup_chat_created": True}, {"channel_chat_created": True}, {"message_auto_delete_timer_changed": MessageAutoDeleteTimerChanged(42)}, {"migrate_to_chat_id": -12345}, {"migrate_from_chat_id": -54321}, { "pinned_message": Message( 7, datetime.utcnow(), Chat(13, "channel"), User(9, "i", False) ) }, {"invoice": Invoice("my invoice", "invoice", "start", "EUR", 243)}, { "successful_payment": SuccessfulPayment( "EUR", 243, "payload", "charge_id", "provider_id", order_info={} ) }, {"connected_website": "http://example.com/"}, {"author_signature": "some_author_sign"}, { "photo": [PhotoSize("photo_id", "unique_id", 50, 50)], "caption": "photo_file", "media_group_id": 1234443322222, }, {"passport_data": PassportData.de_json(RAW_PASSPORT_DATA, None)}, { "poll": Poll( id="abc", question="What is this?", options=[PollOption(text="a", voter_count=1), PollOption(text="b", voter_count=2)], is_closed=False, total_voter_count=0, is_anonymous=False, type=Poll.REGULAR, allows_multiple_answers=True, explanation_entities=[], ) }, { "text": "a text message", "reply_markup": { "inline_keyboard": [ [ {"text": "start", "url": "http://google.com"}, {"text": "next", "callback_data": "abcd"}, ], [{"text": "Cancel", "callback_data": "Cancel"}], ] }, }, {"dice": Dice(4, "🎲")}, {"via_bot": User(9, "A_Bot", True)}, { "proximity_alert_triggered": ProximityAlertTriggered( User(1, "John", False), User(2, "Doe", False), 42 ) }, {"video_chat_scheduled": VideoChatScheduled(datetime.utcnow())}, {"video_chat_started": VideoChatStarted()}, {"video_chat_ended": VideoChatEnded(100)}, { "video_chat_participants_invited": VideoChatParticipantsInvited( [User(1, "Rem", False), User(2, "Emilia", False)] ) }, {"sender_chat": Chat(-123, "discussion_channel")}, {"is_automatic_forward": True}, {"has_protected_content": True}, { "entities": [ MessageEntity(MessageEntity.BOLD, 0, 1), MessageEntity(MessageEntity.TEXT_LINK, 2, 3, url="https://ptb.org"), ] }, {"web_app_data": WebAppData("some_data", "some_button_text")}, {"message_thread_id": 123}, {"users_shared": UsersShared(1, users=[SharedUser(2, "user2"), SharedUser(3, "user3")])}, {"chat_shared": ChatShared(3, 4)}, { "giveaway": Giveaway( chats=[Chat(1, Chat.SUPERGROUP)], winners_selection_date=datetime.utcnow().replace(microsecond=0), winner_count=5, ) }, {"giveaway_created": GiveawayCreated(prize_star_count=99)}, { "giveaway_winners": GiveawayWinners( chat=Chat(1, Chat.CHANNEL), giveaway_message_id=123456789, winners_selection_date=datetime.utcnow().replace(microsecond=0), winner_count=42, winners=[User(1, "user1", False), User(2, "user2", False)], ) }, { "giveaway_completed": GiveawayCompleted( winner_count=42, unclaimed_prize_count=4, giveaway_message=make_message(text="giveaway_message"), ) }, { "link_preview_options": LinkPreviewOptions( is_disabled=True, url="https://python-telegram-bot.org", prefer_small_media=True, prefer_large_media=True, show_above_text=True, ) }, { "external_reply": ExternalReplyInfo( MessageOriginChat(datetime.utcnow(), Chat(1, Chat.PRIVATE)) ) }, {"quote": TextQuote("a text quote", 1)}, {"forward_origin": MessageOriginChat(datetime.utcnow(), Chat(1, Chat.PRIVATE))}, {"reply_to_story": Story(Chat(1, Chat.PRIVATE), 0)}, {"boost_added": ChatBoostAdded(100)}, {"sender_boost_count": 1}, {"is_from_offline": True}, {"sender_business_bot": User(1, "BusinessBot", True)}, {"business_connection_id": "123456789"}, {"chat_background_set": ChatBackground(type=BackgroundTypeChatTheme("ice"))}, {"effect_id": "123456789"}, {"show_caption_above_media": True}, {"paid_media": PaidMediaInfo(5, [PaidMediaPreview(10, 10, 10)])}, {"refunded_payment": RefundedPayment("EUR", 243, "payload", "charge_id", "provider_id")}, ], ids=[ "reply", "edited", "text", "caption_entities", "audio", "document", "animation", "game", "photo", "sticker", "story", "video", "voice", "video_note", "new_members", "contact", "location", "venue", "left_member", "new_title", "new_photo", "delete_photo", "group_created", "supergroup_created", "channel_created", "message_auto_delete_timer_changed", "migrated_to", "migrated_from", "pinned", "invoice", "successful_payment", "connected_website", "author_signature", "photo_from_media_group", "passport_data", "poll", "reply_markup", "dice", "via_bot", "proximity_alert_triggered", "video_chat_scheduled", "video_chat_started", "video_chat_ended", "video_chat_participants_invited", "sender_chat", "is_automatic_forward", "has_protected_content", "entities", "web_app_data", "message_thread_id", "users_shared", "chat_shared", "giveaway", "giveaway_created", "giveaway_winners", "giveaway_completed", "link_preview_options", "external_reply", "quote", "forward_origin", "reply_to_story", "boost_added", "sender_boost_count", "sender_business_bot", "business_connection_id", "is_from_offline", "chat_background_set", "effect_id", "show_caption_above_media", "paid_media", "refunded_payment", ], ) def message_params(bot, request): message = Message( message_id=MessageTestBase.id_, from_user=MessageTestBase.from_user, date=MessageTestBase.date, chat=MessageTestBase.chat, **request.param, ) message.set_bot(bot) return message class MessageTestBase: id_ = 1 from_user = User(2, "testuser", False) date = datetime.utcnow() chat = Chat(3, "private") test_entities = [ {"length": 4, "offset": 10, "type": "bold"}, {"length": 3, "offset": 16, "type": "italic"}, {"length": 3, "offset": 20, "type": "italic"}, {"length": 4, "offset": 25, "type": "code"}, {"length": 5, "offset": 31, "type": "text_link", "url": "http://github.com/ab_"}, { "length": 12, "offset": 38, "type": "text_mention", "user": User(123456789, "mentioned user", False), }, {"length": 3, "offset": 55, "type": "pre", "language": "python"}, {"length": 21, "offset": 60, "type": "url"}, ] test_text = "Test for trgh nested in italic. Python pre. Spoiled. " "👍.\nMultiline\nblock quote\nwith nested.\n\nMultiline\nexpandable\nblock quote." ) test_message = Message( message_id=1, from_user=None, date=None, chat=None, text=test_text, entities=[MessageEntity(**e) for e in test_entities], caption=test_text, caption_entities=[MessageEntity(**e) for e in test_entities], ) test_message_v2 = Message( message_id=1, from_user=None, date=None, chat=None, text=test_text_v2, entities=[MessageEntity(**e) for e in test_entities_v2], caption=test_text_v2, caption_entities=[MessageEntity(**e) for e in test_entities_v2], ) class TestMessageWithoutRequest(MessageTestBase): @staticmethod async def check_quote_parsing( message: Message, method, bot_method_name: str, args, monkeypatch ): """Used in testing reply_* below. Makes sure that quote and do_quote are handled correctly """ with contextlib.suppress(TypeError): # for newer methods that don't have the deprecated argument with pytest.raises(ValueError, match="`quote` and `do_quote` are mutually exclusive"): await method(*args, quote=True, do_quote=True) # for newer methods that don't have the deprecated argument with pytest.warns(PTBDeprecationWarning, match="`quote` parameter is deprecated"): await method(*args, quote=True) with pytest.raises( ValueError, match="`reply_to_message_id` and `reply_parameters` are mutually exclusive.", ): await method(*args, reply_to_message_id=42, reply_parameters=42) async def make_assertion(*args, **kwargs): return kwargs.get("chat_id"), kwargs.get("reply_parameters") monkeypatch.setattr(message.get_bot(), bot_method_name, make_assertion) for param in ("quote", "do_quote"): with contextlib.suppress(TypeError): # for newer methods that don't have the deprecated argument chat_id, reply_parameters = await method(*args, **{param: True}) if chat_id != message.chat.id: pytest.fail(f"chat_id is {chat_id} but should be {message.chat.id}") if reply_parameters is None or reply_parameters.message_id != message.message_id: pytest.fail( f"reply_parameters is {reply_parameters} " "but should be {message.message_id}" ) input_chat_id = object() input_reply_parameters = ReplyParameters(message_id=1, chat_id=42) chat_id, reply_parameters = await method( *args, do_quote={"chat_id": input_chat_id, "reply_parameters": input_reply_parameters} ) if chat_id is not input_chat_id: pytest.fail(f"chat_id is {chat_id} but should be {chat_id}") if reply_parameters is not input_reply_parameters: pytest.fail(f"reply_parameters is {reply_parameters} but should be {reply_parameters}") input_parameters_2 = ReplyParameters(message_id=2, chat_id=43) chat_id, reply_parameters = await method( *args, reply_parameters=input_parameters_2, # passing these here to make sure that `reply_parameters` has higher priority do_quote={"chat_id": input_chat_id, "reply_parameters": input_reply_parameters}, ) if chat_id is not message.chat.id: pytest.fail(f"chat_id is {chat_id} but should be {message.chat.id}") if reply_parameters is not input_parameters_2: pytest.fail( f"reply_parameters is {reply_parameters} but should be {input_parameters_2}" ) chat_id, reply_parameters = await method( *args, reply_to_message_id=42, # passing these here to make sure that `reply_to_message_id` has higher priority do_quote={"chat_id": input_chat_id, "reply_parameters": input_reply_parameters}, ) if chat_id != message.chat.id: pytest.fail(f"chat_id is {chat_id} but should be {message.chat.id}") if reply_parameters is None or reply_parameters.message_id != 42: pytest.fail(f"reply_parameters is {reply_parameters} but should be 42") @staticmethod async def check_thread_id_parsing( message: Message, method, bot_method_name: str, args, monkeypatch ): """Used in testing reply_* below. Makes sure that meassage_thread_id is parsed correctly.""" async def extract_message_thread_id(*args, **kwargs): return kwargs.get("message_thread_id") monkeypatch.setattr(message.get_bot(), bot_method_name, extract_message_thread_id) for is_topic_message in (True, False): message.is_topic_message = is_topic_message message.message_thread_id = None message_thread_id = await method(*args) assert message_thread_id is None message.message_thread_id = 99 message_thread_id = await method(*args) assert message_thread_id == (99 if is_topic_message else None) message_thread_id = await method(*args, message_thread_id=50) assert message_thread_id == 50 message_thread_id = await method(*args, message_thread_id=None) assert message_thread_id is None if bot_method_name == "send_chat_action": return message_thread_id = await method( *args, do_quote=message.build_reply_arguments( target_chat_id=123, ), ) assert message_thread_id is None for target_chat_id in (message.chat_id, message.chat.username): message_thread_id = await method( *args, do_quote=message.build_reply_arguments( target_chat_id=target_chat_id, ), ) assert message_thread_id == (message.message_thread_id if is_topic_message else None) def test_slot_behaviour(self): message = Message( message_id=MessageTestBase.id_, date=MessageTestBase.date, chat=copy(MessageTestBase.chat), from_user=copy(MessageTestBase.from_user), ) for attr in message.__slots__: assert getattr(message, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(message)) == len(set(mro_slots(message))), "duplicate slot" def test_all_possibilities_de_json_and_to_dict(self, offline_bot, message_params): new = Message.de_json(message_params.to_dict(), offline_bot) assert new.api_kwargs == {} assert new.to_dict() == message_params.to_dict() # Checking that none of the attributes are dicts is a best effort approach to ensure that # de_json converts everything to proper classes without having to write special tests for # every single case for slot in new.__slots__: assert not isinstance(new[slot], dict) def test_de_json_localization(self, offline_bot, raw_bot, tz_bot): json_dict = { "message_id": 12, "from_user": None, "date": int(datetime.now().timestamp()), "chat": None, "edit_date": int(datetime.now().timestamp()), } message_raw = Message.de_json(json_dict, raw_bot) message_bot = Message.de_json(json_dict, offline_bot) message_tz = Message.de_json(json_dict, tz_bot) # comparing utcoffsets because comparing timezones is unpredicatable date_offset = message_tz.date.utcoffset() date_tz_bot_offset = tz_bot.defaults.tzinfo.utcoffset(message_tz.date.replace(tzinfo=None)) edit_date_offset = message_tz.edit_date.utcoffset() edit_date_tz_bot_offset = tz_bot.defaults.tzinfo.utcoffset( message_tz.edit_date.replace(tzinfo=None) ) assert message_raw.date.tzinfo == UTC assert message_bot.date.tzinfo == UTC assert date_offset == date_tz_bot_offset assert message_raw.edit_date.tzinfo == UTC assert message_bot.edit_date.tzinfo == UTC assert edit_date_offset == edit_date_tz_bot_offset def test_de_json_api_kwargs_backward_compatibility(self, offline_bot, message_params): message_dict = message_params.to_dict() keys = ( "user_shared", "forward_from", "forward_from_chat", "forward_from_message_id", "forward_signature", "forward_sender_name", "forward_date", ) for key in keys: message_dict[key] = key message = Message.de_json(message_dict, offline_bot) assert message.api_kwargs == {key: key for key in keys} def test_equality(self): id_ = 1 a = Message(id_, self.date, self.chat, from_user=self.from_user) b = Message(id_, self.date, self.chat, from_user=self.from_user) c = Message(id_, self.date, Chat(123, Chat.GROUP), from_user=User(0, "", False)) d = Message(0, self.date, self.chat, from_user=self.from_user) e = Update(id_) assert a == b assert hash(a) == hash(b) assert a is not b assert a != c assert hash(a) != hash(c) assert a != d assert hash(a) != hash(d) assert a != e assert hash(a) != hash(e) def test_bool(self, message, recwarn): # Relevant as long as we override MaybeInaccessibleMessage.__bool__ # Can be removed once that's removed assert bool(message) is True assert len(recwarn) == 0 async def test_parse_entity(self): text = ( b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" b"\\u200d\\U0001f467\\U0001f431http://google.com" ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[entity], ) assert message.parse_entity(entity) == "http://google.com" with pytest.raises(RuntimeError, match="Message has no"): Message(message_id=1, date=self.date, chat=self.chat).parse_entity(entity) async def test_parse_caption_entity(self): caption = ( b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" b"\\u200d\\U0001f467\\U0001f431http://google.com" ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[entity], ) assert message.parse_caption_entity(entity) == "http://google.com" with pytest.raises(RuntimeError, match="Message has no"): Message(message_id=1, date=self.date, chat=self.chat).parse_entity(entity) async def test_parse_entities(self): text = ( b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" b"\\u200d\\U0001f467\\U0001f431http://google.com" ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[entity_2, entity], ) assert message.parse_entities(MessageEntity.URL) == {entity: "http://google.com"} assert message.parse_entities() == {entity: "http://google.com", entity_2: "h"} async def test_parse_caption_entities(self): text = ( b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" b"\\u200d\\U0001f467\\U0001f431http://google.com" ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=text, caption_entities=[entity_2, entity], ) assert message.parse_caption_entities(MessageEntity.URL) == {entity: "http://google.com"} assert message.parse_caption_entities() == { entity: "http://google.com", entity_2: "h", } def test_text_html_simple(self): test_html_string = ( "Test for <bold, ita_lic, " r"\`code, " r'links, ' 'text-mention and ' r"
`\pre
. http://google.com " "and bold nested in strk>trgh nested in italic. " '
Python pre
. ' 'Spoiled. ' '👍.\n' "
Multiline\nblock quote\nwith nested.
\n\n" "
Multiline\nexpandable\nblock quote.
" ) text_html = self.test_message_v2.text_html assert text_html == test_html_string def test_text_html_empty(self, message): message.text = None message.caption = "test" assert message.text_html is None def test_text_html_urled(self): test_html_string = ( "Test for <bold, ita_lic, " r"\`code, " r'links, ' 'text-mention and ' r'
`\pre
. http://google.com ' "and bold nested in strk>trgh nested in italic. " '
Python pre
. ' 'Spoiled. ' '👍.\n' "
Multiline\nblock quote\nwith nested.
\n\n" "
Multiline\nexpandable\nblock quote.
" ) text_html = self.test_message_v2.text_html_urled assert text_html == test_html_string def test_text_markdown_simple(self): test_md_string = ( r"Test for <*bold*, _ita_\__lic_, `code`, " "[links](http://github.com/ab_), " "[text-mention](tg://user?id=123456789) and ```python\npre```. " r"http://google.com/ab\_" ) text_markdown = self.test_message.text_markdown assert text_markdown == test_md_string def test_text_markdown_v2_simple(self): test_md_string = ( r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " "[links](http://github.com/abc\\\\\\)def), " "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " "```python\nPython pre```\\. ||Spoiled||\\. ![👍](tg://emoji?id=1)\\.\n" ">Multiline\n" ">block quote\n" r">with *nested*\." "\n\n>Multiline\n" ">expandable\n" r">block quote\.||" ) text_markdown = self.test_message_v2.text_markdown_v2 assert text_markdown == test_md_string @pytest.mark.parametrize( "entity_type", [ MessageEntity.UNDERLINE, MessageEntity.STRIKETHROUGH, MessageEntity.SPOILER, MessageEntity.BLOCKQUOTE, MessageEntity.CUSTOM_EMOJI, ], ) def test_text_markdown_new_in_v2(self, message, entity_type): message.text = "test" message.entities = [ MessageEntity(MessageEntity.BOLD, offset=0, length=4), MessageEntity(MessageEntity.ITALIC, offset=0, length=4), ] with pytest.raises(ValueError, match="Nested entities are not supported for"): assert message.text_markdown message.entities = [MessageEntity(entity_type, offset=0, length=4)] with pytest.raises(ValueError, match="entities are not supported for"): message.text_markdown message.entities = [] def test_text_markdown_empty(self, message): message.text = None message.caption = "test" assert message.text_markdown is None assert message.text_markdown_v2 is None def test_text_markdown_urled(self): test_md_string = ( r"Test for <*bold*, _ita_\__lic_, `code`, " "[links](http://github.com/ab_), " "[text-mention](tg://user?id=123456789) and ```python\npre```. " "[http://google.com/ab_](http://google.com/ab_)" ) text_markdown = self.test_message.text_markdown_urled assert text_markdown == test_md_string def test_text_markdown_v2_urled(self): test_md_string = ( r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " "[links](http://github.com/abc\\\\\\)def), " "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " r"[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ " "nested in* italic_\\. ```python\nPython pre```\\. ||Spoiled||\\. " "![👍](tg://emoji?id=1)\\.\n" ">Multiline\n" ">block quote\n" r">with *nested*\." "\n\n>Multiline\n" ">expandable\n" r">block quote\.||" ) text_markdown = self.test_message_v2.text_markdown_v2_urled assert text_markdown == test_md_string def test_text_html_emoji(self): text = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") expected = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[bold_entity], ) assert expected == message.text_html def test_text_markdown_emoji(self): text = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, self.date, self.chat, self.from_user, text=text, entities=[bold_entity] ) assert expected == message.text_markdown @pytest.mark.parametrize( "type_", argvalues=[ "text_markdown", "text_markdown_urled", ], ) def test_text_custom_emoji_md_v1(self, type_, recwarn): text = "Look a custom emoji: 😎" emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[emoji_entity], ) with pytest.raises(ValueError, match="Custom Emoji entities are not supported for"): getattr(message, type_) @pytest.mark.parametrize( "type_", argvalues=[ "text_markdown_v2", "text_markdown_v2_urled", ], ) def test_text_custom_emoji_md_v2(self, type_): text = "Look a custom emoji: 😎" expected = "Look a custom emoji: ![😎](tg://emoji?id=5472409228461217725)" emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[emoji_entity], ) assert expected == message[type_] @pytest.mark.parametrize( "type_", argvalues=[ "text_html", "text_html_urled", ], ) def test_text_custom_emoji_html(self, type_): text = "Look a custom emoji: 😎" expected = 'Look a custom emoji: 😎' emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, text=text, entities=[emoji_entity], ) assert expected == message[type_] def test_caption_html_simple(self): test_html_string = ( "Test for <bold, ita_lic, " r"\`code, " r'links, ' 'text-mention and ' r"
`\pre
. http://google.com " "and bold nested in strk>trgh nested in italic. " '
Python pre
. ' 'Spoiled. ' '👍.\n' "
Multiline\nblock quote\nwith nested.
\n\n" "
Multiline\nexpandable\nblock quote.
" ) caption_html = self.test_message_v2.caption_html assert caption_html == test_html_string def test_caption_html_empty(self, message): message.text = "test" message.caption = None assert message.caption_html is None def test_caption_html_urled(self): test_html_string = ( "Test for <bold, ita_lic, " r"\`code, " r'links, ' 'text-mention and ' r'
`\pre
. http://google.com ' "and bold nested in strk>trgh nested in italic. " '
Python pre
. ' 'Spoiled. ' '👍.\n' "
Multiline\nblock quote\nwith nested.
\n\n" "
Multiline\nexpandable\nblock quote.
" ) caption_html = self.test_message_v2.caption_html_urled assert caption_html == test_html_string def test_caption_markdown_simple(self): test_md_string = ( r"Test for <*bold*, _ita_\__lic_, `code`, " "[links](http://github.com/ab_), " "[text-mention](tg://user?id=123456789) and ```python\npre```. " r"http://google.com/ab\_" ) caption_markdown = self.test_message.caption_markdown assert caption_markdown == test_md_string def test_caption_markdown_v2_simple(self): test_md_string = ( r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " "[links](http://github.com/abc\\\\\\)def), " "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " "```python\nPython pre```\\. ||Spoiled||\\. ![👍](tg://emoji?id=1)\\.\n" ">Multiline\n" ">block quote\n" r">with *nested*\." "\n\n>Multiline\n" ">expandable\n" r">block quote\.||" ) caption_markdown = self.test_message_v2.caption_markdown_v2 assert caption_markdown == test_md_string def test_caption_markdown_empty(self, message): message.text = "test" message.caption = None assert message.caption_markdown is None assert message.caption_markdown_v2 is None def test_caption_markdown_urled(self): test_md_string = ( r"Test for <*bold*, _ita_\__lic_, `code`, " "[links](http://github.com/ab_), " "[text-mention](tg://user?id=123456789) and ```python\npre```. " "[http://google.com/ab_](http://google.com/ab_)" ) caption_markdown = self.test_message.caption_markdown_urled assert caption_markdown == test_md_string def test_caption_markdown_v2_urled(self): test_md_string = ( r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " "[links](http://github.com/abc\\\\\\)def), " "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " r"[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ " "nested in* italic_\\. ```python\nPython pre```\\. ||Spoiled||\\. " "![👍](tg://emoji?id=1)\\.\n" ">Multiline\n" ">block quote\n" r">with *nested*\." "\n\n>Multiline\n" ">expandable\n" r">block quote\.||" ) caption_markdown = self.test_message_v2.caption_markdown_v2_urled assert caption_markdown == test_md_string def test_caption_html_emoji(self): caption = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") expected = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[bold_entity], ) assert expected == message.caption_html def test_caption_markdown_emoji(self): caption = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[bold_entity], ) assert expected == message.caption_markdown @pytest.mark.parametrize( "type_", argvalues=[ "caption_markdown", "caption_markdown_urled", ], ) def test_caption_custom_emoji_md_v1(self, type_, recwarn): caption = "Look a custom emoji: 😎" emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[emoji_entity], ) with pytest.raises(ValueError, match="Custom Emoji entities are not supported for"): getattr(message, type_) @pytest.mark.parametrize( "type_", argvalues=[ "caption_markdown_v2", "caption_markdown_v2_urled", ], ) def test_caption_custom_emoji_md_v2(self, type_): caption = "Look a custom emoji: 😎" expected = "Look a custom emoji: ![😎](tg://emoji?id=5472409228461217725)" emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[emoji_entity], ) assert expected == message[type_] @pytest.mark.parametrize( "type_", argvalues=[ "caption_html", "caption_html_urled", ], ) def test_caption_custom_emoji_html(self, type_): caption = "Look a custom emoji: 😎" expected = 'Look a custom emoji: 😎' emoji_entity = MessageEntity( type=MessageEntity.CUSTOM_EMOJI, offset=21, length=2, custom_emoji_id="5472409228461217725", ) message = Message( 1, from_user=self.from_user, date=self.date, chat=self.chat, caption=caption, caption_entities=[emoji_entity], ) assert expected == message[type_] async def test_parse_entities_url_emoji(self): url = b"http://github.com/?unicode=\\u2713\\U0001f469".decode("unicode-escape") text = "some url" link_entity = MessageEntity(type=MessageEntity.URL, offset=0, length=8, url=url) message = Message( 1, self.from_user, self.date, self.chat, text=text, entities=[link_entity] ) assert message.parse_entities() == {link_entity: text} assert next(iter(message.parse_entities())).url == url def test_chat_id(self, message): assert message.chat_id == message.chat.id def test_id(self, message): assert message.message_id == message.id @pytest.mark.parametrize("type_", argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) def test_link_with_username(self, message, type_): message.chat.username = "username" message.chat.type = type_ assert message.link == f"https://t.me/{message.chat.username}/{message.message_id}" @pytest.mark.parametrize( ("type_", "id_"), argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)] ) def test_link_with_id(self, message, type_, id_): message.chat.username = None message.chat.id = id_ message.chat.type = type_ # The leading - for group ids/ -100 for supergroup ids isn't supposed to be in the link assert message.link == f"https://t.me/c/{3}/{message.message_id}" @pytest.mark.parametrize("type_", argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) def test_link_with_topics(self, message, type_): message.chat.username = None message.chat.id = -1003 message.chat.type = type_ message.is_topic_message = True message.message_thread_id = 123 assert message.link == f"https://t.me/c/3/{message.message_id}?thread=123" @pytest.mark.parametrize("type_", argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) def test_link_with_reply(self, message, type_): message.chat.username = None message.chat.id = -1003 message.chat.type = type_ message.reply_to_message = Message(7, self.from_user, self.date, self.chat, text="Reply") message.message_thread_id = 123 assert message.link == f"https://t.me/c/3/{message.message_id}?thread=123" @pytest.mark.parametrize(("id_", "username"), argvalues=[(None, "username"), (-3, None)]) def test_link_private_chats(self, message, id_, username): message.chat.type = Chat.PRIVATE message.chat.id = id_ message.chat.username = username assert message.link is None message.chat.type = Chat.GROUP assert message.link is None def test_effective_attachment(self, message_params): # This list is hard coded on purpose because just using constants.MessageAttachmentType # (which is used in Message.effective_message) wouldn't find any mistakes expected_attachment_types = [ "animation", "audio", "contact", "dice", "document", "game", "invoice", "location", "paid_media", "passport_data", "photo", "poll", "sticker", "story", "successful_payment", "video", "video_note", "voice", "venue", ] for _ in range(3): # We run the same test multiple times to make sure that the caching is tested attachment = message_params.effective_attachment if attachment: condition = any( message_params[message_type] is attachment for message_type in expected_attachment_types ) assert condition, "Got effective_attachment for unexpected type" else: condition = any( message_params[message_type] for message_type in expected_attachment_types ) assert not condition, "effective_attachment was None even though it should not be" def test_compute_quote_position_and_entities_false_index(self, message): message.text = "AA" with pytest.raises( ValueError, match="You requested the 5-th occurrence of 'A', " "but this text appears only 2 times.", ): message.compute_quote_position_and_entities("A", 5) def test_compute_quote_position_and_entities_no_text_or_caption(self, message): message.text = None message.caption = None with pytest.raises( RuntimeError, match="This message has neither text nor caption.", ): message.compute_quote_position_and_entities("A", 5) @pytest.mark.parametrize( ("text", "quote", "index", "expected"), argvalues=[ ("AA", "A", None, 0), ("AA", "A", 0, 0), ("AA", "A", 1, 1), ("ABC ABC ABC ABC", "ABC", None, 0), ("ABC ABC ABC ABC", "ABC", 0, 0), ("ABC ABC ABC ABC", "ABC", 3, 12), ("👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧", "👨‍👨‍👧", 0, 0), ("👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧", "👨‍👨‍👧", 3, 24), ("👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧", "👨", 1, 3), ("👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧👨‍👨‍👧", "👧", 2, 22), ], ) @pytest.mark.parametrize("caption", [True, False]) def test_compute_quote_position_and_entities_position( self, message, text, quote, index, expected, caption ): if caption: message.caption = text message.text = None else: message.text = text message.caption = None assert message.compute_quote_position_and_entities(quote, index)[0] == expected def test_compute_quote_position_and_entities_entities(self, message): message.text = "A A A" message.entities = () assert message.compute_quote_position_and_entities("A", 0)[1] is None message.entities = ( # covers complete string MessageEntity(type=MessageEntity.BOLD, offset=0, length=6), # covers first 2 As only MessageEntity(type=MessageEntity.ITALIC, offset=0, length=3), # covers second 2 As only MessageEntity(type=MessageEntity.UNDERLINE, offset=2, length=3), # covers middle A only MessageEntity(type=MessageEntity.STRIKETHROUGH, offset=2, length=1), # covers only whitespace, should be ignored MessageEntity(type=MessageEntity.CODE, offset=1, length=1), ) assert message.compute_quote_position_and_entities("A", 0)[1] == ( MessageEntity(type=MessageEntity.BOLD, offset=0, length=1), MessageEntity(type=MessageEntity.ITALIC, offset=0, length=1), ) assert message.compute_quote_position_and_entities("A", 1)[1] == ( MessageEntity(type=MessageEntity.BOLD, offset=0, length=1), MessageEntity(type=MessageEntity.ITALIC, offset=0, length=1), MessageEntity(type=MessageEntity.UNDERLINE, offset=0, length=1), MessageEntity(type=MessageEntity.STRIKETHROUGH, offset=0, length=1), ) assert message.compute_quote_position_and_entities("A", 2)[1] == ( MessageEntity(type=MessageEntity.BOLD, offset=0, length=1), MessageEntity(type=MessageEntity.UNDERLINE, offset=0, length=1), ) @pytest.mark.parametrize( ("target_chat_id", "expected"), argvalues=[ (None, 3), (3, 3), (-1003, -1003), ("@username", "@username"), ], ) def test_build_reply_arguments_chat_id_and_message_id(self, message, target_chat_id, expected): message.chat.id = 3 reply_kwargs = message.build_reply_arguments(target_chat_id=target_chat_id) assert reply_kwargs["chat_id"] == expected assert reply_kwargs["reply_parameters"].chat_id == (None if expected == 3 else 3) assert reply_kwargs["reply_parameters"].message_id == message.message_id @pytest.mark.parametrize( ("target_chat_id", "message_thread_id", "expected"), argvalues=[ (None, None, True), (None, 123, True), (None, 0, False), (None, -1, False), (3, None, True), (3, 123, True), (3, 0, False), (3, -1, False), (-1003, None, False), (-1003, 123, False), (-1003, 0, False), (-1003, -1, False), ("@username", None, True), ("@username", 123, True), ("@username", 0, False), ("@username", -1, False), ("@other_username", None, False), ("@other_username", 123, False), ("@other_username", 0, False), ("@other_username", -1, False), ], ) def test_build_reply_arguments_aswr( self, message, target_chat_id, message_thread_id, expected ): message.chat.id = 3 message.chat.username = "username" message.message_thread_id = 123 assert ( message.build_reply_arguments( target_chat_id=target_chat_id, message_thread_id=message_thread_id )["reply_parameters"].allow_sending_without_reply is not None ) == expected assert ( message.build_reply_arguments( target_chat_id=target_chat_id, message_thread_id=message_thread_id, allow_sending_without_reply="custom", )["reply_parameters"].allow_sending_without_reply ) == ("custom" if expected else None) def test_build_reply_arguments_quote(self, message, monkeypatch): reply_parameters = message.build_reply_arguments()["reply_parameters"] assert reply_parameters.quote is None assert reply_parameters.quote_entities == () assert reply_parameters.quote_position is None assert not reply_parameters.quote_parse_mode quote_obj = object() quote_index = object() quote_entities = (object(), object()) quote_position = object() def mock_compute(quote, index): if quote is quote_obj and index is quote_index: return quote_position, quote_entities return False, False monkeypatch.setattr(message, "compute_quote_position_and_entities", mock_compute) reply_parameters = message.build_reply_arguments(quote=quote_obj, quote_index=quote_index)[ "reply_parameters" ] assert reply_parameters.quote is quote_obj assert reply_parameters.quote_entities is quote_entities assert reply_parameters.quote_position is quote_position assert not reply_parameters.quote_parse_mode async def test_reply_text(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id text = kwargs["text"] == "test" return id_ and text assert check_shortcut_signature( Message.reply_text, Bot.send_message, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_text, message.get_bot(), "send_message", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_text, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_message", make_assertion) assert await message.reply_text("test") await self.check_quote_parsing( message, message.reply_text, "send_message", ["test"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_text, "send_message", ["test"], monkeypatch ) async def test_reply_markdown(self, monkeypatch, message): test_md_string = ( r"Test for <*bold*, _ita_\__lic_, `code`, " "[links](http://github.com/ab_), " "[text-mention](tg://user?id=123456789) and ```python\npre```. " r"http://google.com/ab\_" ) async def make_assertion(*_, **kwargs): cid = kwargs["chat_id"] == message.chat_id markdown_text = kwargs["text"] == test_md_string markdown_enabled = kwargs["parse_mode"] == ParseMode.MARKDOWN return all([cid, markdown_text, markdown_enabled]) assert check_shortcut_signature( Message.reply_markdown, Bot.send_message, ["chat_id", "parse_mode", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_text, message.get_bot(), "send_message", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_text, message.get_bot(), no_default_kwargs={"message_thread_id"} ) text_markdown = self.test_message.text_markdown assert text_markdown == test_md_string monkeypatch.setattr(message.get_bot(), "send_message", make_assertion) assert await message.reply_markdown(self.test_message.text_markdown) await self.check_thread_id_parsing( message, message.reply_markdown, "send_message", ["test"], monkeypatch ) async def test_reply_markdown_v2(self, monkeypatch, message): test_md_string = ( r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " "[links](http://github.com/abc\\\\\\)def), " "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " "```python\nPython pre```\\. ||Spoiled||\\. ![👍](tg://emoji?id=1)\\.\n" ">Multiline\n" ">block quote\n" r">with *nested*\." "\n\n>Multiline\n" ">expandable\n" r">block quote\.||" ) async def make_assertion(*_, **kwargs): cid = kwargs["chat_id"] == message.chat_id markdown_text = kwargs["text"] == test_md_string markdown_enabled = kwargs["parse_mode"] == ParseMode.MARKDOWN_V2 return all([cid, markdown_text, markdown_enabled]) assert check_shortcut_signature( Message.reply_markdown_v2, Bot.send_message, ["chat_id", "parse_mode", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_text, message.get_bot(), "send_message", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_text, message.get_bot(), no_default_kwargs={"message_thread_id"} ) text_markdown = self.test_message_v2.text_markdown_v2 assert text_markdown == test_md_string monkeypatch.setattr(message.get_bot(), "send_message", make_assertion) assert await message.reply_markdown_v2(self.test_message_v2.text_markdown_v2) await self.check_quote_parsing( message, message.reply_markdown_v2, "send_message", [test_md_string], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_markdown_v2, "send_message", ["test"], monkeypatch ) async def test_reply_html(self, monkeypatch, message): test_html_string = ( "Test for <bold, ita_lic, " r"\`code, " r'links, ' 'text-mention and ' r"
`\pre
. http://google.com " "and bold nested in strk>trgh nested in italic. " '
Python pre
. ' 'Spoiled. ' '👍.\n' "
Multiline\nblock quote\nwith nested.
\n\n" "
Multiline\nexpandable\nblock quote.
" ) async def make_assertion(*_, **kwargs): cid = kwargs["chat_id"] == message.chat_id html_text = kwargs["text"] == test_html_string html_enabled = kwargs["parse_mode"] == ParseMode.HTML return all([cid, html_text, html_enabled]) assert check_shortcut_signature( Message.reply_html, Bot.send_message, ["chat_id", "parse_mode", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_text, message.get_bot(), "send_message", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_text, message.get_bot(), no_default_kwargs={"message_thread_id"} ) text_html = self.test_message_v2.text_html assert text_html == test_html_string monkeypatch.setattr(message.get_bot(), "send_message", make_assertion) assert await message.reply_html(self.test_message_v2.text_html) await self.check_quote_parsing( message, message.reply_html, "send_message", [test_html_string], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_html, "send_message", ["test"], monkeypatch ) async def test_reply_media_group(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id media = kwargs["media"] == "reply_media_group" return id_ and media assert check_shortcut_signature( Message.reply_media_group, Bot.send_media_group, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_media_group, message.get_bot(), "send_media_group", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_media_group, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_media_group", make_assertion) assert await message.reply_media_group(media="reply_media_group") await self.check_quote_parsing( message, message.reply_media_group, "send_media_group", ["reply_media_group"], monkeypatch, ) await self.check_thread_id_parsing( message, message.reply_media_group, "send_media_group", ["reply_media_group"], monkeypatch, ) async def test_reply_photo(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id photo = kwargs["photo"] == "test_photo" return id_ and photo assert check_shortcut_signature( Message.reply_photo, Bot.send_photo, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_photo, message.get_bot(), "send_photo", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_photo, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_photo", make_assertion) assert await message.reply_photo(photo="test_photo") await self.check_quote_parsing( message, message.reply_photo, "send_photo", ["test_photo"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_photo, "send_photo", ["test_photo"], monkeypatch ) async def test_reply_audio(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id audio = kwargs["audio"] == "test_audio" return id_ and audio assert check_shortcut_signature( Message.reply_audio, Bot.send_audio, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_audio, message.get_bot(), "send_audio", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_audio, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_audio", make_assertion) assert await message.reply_audio(audio="test_audio") await self.check_quote_parsing( message, message.reply_audio, "send_audio", ["test_audio"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_audio, "send_audio", ["test_audio"], monkeypatch ) async def test_reply_document(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id document = kwargs["document"] == "test_document" return id_ and document assert check_shortcut_signature( Message.reply_document, Bot.send_document, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_document, message.get_bot(), "send_document", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_document, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_document", make_assertion) assert await message.reply_document(document="test_document") await self.check_quote_parsing( message, message.reply_document, "send_document", ["test_document"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_document, "send_document", ["test_document"], monkeypatch ) async def test_reply_animation(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id animation = kwargs["animation"] == "test_animation" return id_ and animation assert check_shortcut_signature( Message.reply_animation, Bot.send_animation, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_animation, message.get_bot(), "send_animation", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_animation, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_animation", make_assertion) assert await message.reply_animation(animation="test_animation") await self.check_quote_parsing( message, message.reply_animation, "send_animation", ["test_animation"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_animation, "send_animation", ["test_animation"], monkeypatch ) async def test_reply_sticker(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id sticker = kwargs["sticker"] == "test_sticker" return id_ and sticker assert check_shortcut_signature( Message.reply_sticker, Bot.send_sticker, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_sticker, message.get_bot(), "send_sticker", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_sticker, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_sticker", make_assertion) assert await message.reply_sticker(sticker="test_sticker") await self.check_quote_parsing( message, message.reply_sticker, "send_sticker", ["test_sticker"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_sticker, "send_sticker", ["test_sticker"], monkeypatch ) async def test_reply_video(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id video = kwargs["video"] == "test_video" return id_ and video assert check_shortcut_signature( Message.reply_video, Bot.send_video, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_video, message.get_bot(), "send_video", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_video, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_video", make_assertion) assert await message.reply_video(video="test_video") await self.check_quote_parsing( message, message.reply_video, "send_video", ["test_video"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_video, "send_video", ["test_video"], monkeypatch ) async def test_reply_video_note(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id video_note = kwargs["video_note"] == "test_video_note" return id_ and video_note assert check_shortcut_signature( Message.reply_video_note, Bot.send_video_note, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_video_note, message.get_bot(), "send_video_note", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_video_note, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_video_note", make_assertion) assert await message.reply_video_note(video_note="test_video_note") await self.check_quote_parsing( message, message.reply_video_note, "send_video_note", ["test_video_note"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_video_note, "send_video_note", ["test_video_note"], monkeypatch ) async def test_reply_voice(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id voice = kwargs["voice"] == "test_voice" return id_ and voice assert check_shortcut_signature( Message.reply_voice, Bot.send_voice, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_voice, message.get_bot(), "send_voice", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_voice, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_voice", make_assertion) assert await message.reply_voice(voice="test_voice") await self.check_quote_parsing( message, message.reply_voice, "send_voice", ["test_voice"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_voice, "send_voice", ["test_voice"], monkeypatch ) async def test_reply_location(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id location = kwargs["location"] == "test_location" return id_ and location assert check_shortcut_signature( Message.reply_location, Bot.send_location, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_location, message.get_bot(), "send_location", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_location, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_location", make_assertion) assert await message.reply_location(location="test_location") await self.check_quote_parsing( message, message.reply_location, "send_location", ["test_location"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_location, "send_location", ["test_location"], monkeypatch ) async def test_reply_venue(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id venue = kwargs["venue"] == "test_venue" return id_ and venue assert check_shortcut_signature( Message.reply_venue, Bot.send_venue, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_venue, message.get_bot(), "send_venue", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_venue, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_venue", make_assertion) assert await message.reply_venue(venue="test_venue") await self.check_quote_parsing( message, message.reply_venue, "send_venue", ["test_venue"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_venue, "send_venue", ["test_venue"], monkeypatch ) async def test_reply_contact(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id contact = kwargs["contact"] == "test_contact" return id_ and contact assert check_shortcut_signature( Message.reply_contact, Bot.send_contact, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_contact, message.get_bot(), "send_contact", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_contact, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_contact", make_assertion) assert await message.reply_contact(contact="test_contact") await self.check_quote_parsing( message, message.reply_contact, "send_contact", ["test_contact"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_contact, "send_contact", ["test_contact"], monkeypatch ) async def test_reply_poll(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id question = kwargs["question"] == "test_poll" options = kwargs["options"] == ["1", "2", "3"] return id_ and question and options assert check_shortcut_signature( Message.reply_poll, Bot.send_poll, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_poll, message.get_bot(), "send_poll", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_poll, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_poll", make_assertion) assert await message.reply_poll(question="test_poll", options=["1", "2", "3"]) await self.check_quote_parsing( message, message.reply_poll, "send_poll", ["test_poll", ["1", "2", "3"]], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_poll, "send_poll", ["test_poll", ["1", "2", "3"]], monkeypatch ) async def test_reply_dice(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id contact = kwargs["disable_notification"] is True return id_ and contact assert check_shortcut_signature( Message.reply_dice, Bot.send_dice, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_dice, message.get_bot(), "send_dice", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_dice, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_dice", make_assertion) assert await message.reply_dice(disable_notification=True) await self.check_quote_parsing( message, message.reply_dice, "send_dice", [], monkeypatch, ) await self.check_thread_id_parsing( message, message.reply_dice, "send_dice", [], monkeypatch ) async def test_reply_action(self, monkeypatch, message: Message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id action = kwargs["action"] == ChatAction.TYPING return id_ and action assert check_shortcut_signature( Message.reply_chat_action, Bot.send_chat_action, ["chat_id", "reply_to_message_id", "business_connection_id"], [], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_chat_action, message.get_bot(), "send_chat_action", shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_chat_action, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_chat_action", make_assertion) assert await message.reply_chat_action(action=ChatAction.TYPING) await self.check_thread_id_parsing( message, message.reply_chat_action, "send_chat_action", [ChatAction.TYPING], monkeypatch, ) async def test_reply_game(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["game_short_name"] == "test_game" ) assert check_shortcut_signature( Message.reply_game, Bot.send_game, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_game, message.get_bot(), "send_game", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_game, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_game", make_assertion) assert await message.reply_game(game_short_name="test_game") await self.check_quote_parsing( message, message.reply_game, "send_game", ["test_game"], monkeypatch ) await self.check_thread_id_parsing( message, message.reply_game, "send_game", ["test_game"], monkeypatch, ) async def test_reply_invoice(self, monkeypatch, message): async def make_assertion(*_, **kwargs): title = kwargs["title"] == "title" description = kwargs["description"] == "description" payload = kwargs["payload"] == "payload" provider_token = kwargs["provider_token"] == "provider_token" currency = kwargs["currency"] == "currency" prices = kwargs["prices"] == "prices" args = title and description and payload and provider_token and currency and prices return kwargs["chat_id"] == message.chat_id and args assert check_shortcut_signature( Message.reply_invoice, Bot.send_invoice, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call( message.reply_invoice, message.get_bot(), "send_invoice", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_invoice, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_invoice", make_assertion) assert await message.reply_invoice( "title", "description", "payload", "provider_token", "currency", "prices", ) await self.check_quote_parsing( message, message.reply_invoice, "send_invoice", ["title", "description", "payload", "provider_token", "currency", "prices"], monkeypatch, ) await self.check_thread_id_parsing( message, message.reply_invoice, "send_invoice", ["title", "description", "payload", "provider_token", "currency", "prices"], monkeypatch, ) @pytest.mark.parametrize(("disable_notification", "protected"), [(False, True), (True, False)]) async def test_forward(self, monkeypatch, message, disable_notification, protected): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == 123456 from_chat = kwargs["from_chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id notification = kwargs["disable_notification"] == disable_notification protected_cont = kwargs["protect_content"] == protected return chat_id and from_chat and message_id and notification and protected_cont assert check_shortcut_signature( Message.forward, Bot.forward_message, ["from_chat_id", "message_id"], [] ) assert await check_shortcut_call(message.forward, message.get_bot(), "forward_message") assert await check_defaults_handling(message.forward, message.get_bot()) monkeypatch.setattr(message.get_bot(), "forward_message", make_assertion) assert await message.forward( 123456, disable_notification=disable_notification, protect_content=protected ) assert not await message.forward(635241) @pytest.mark.parametrize(("disable_notification", "protected"), [(True, False), (False, True)]) async def test_copy(self, monkeypatch, message, disable_notification, protected): keyboard = [[1, 2]] async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == 123456 from_chat = kwargs["from_chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id notification = kwargs["disable_notification"] == disable_notification protected_cont = kwargs["protect_content"] == protected if kwargs.get("reply_markup") is not None: reply_markup = kwargs["reply_markup"] is keyboard else: reply_markup = True return ( chat_id and from_chat and message_id and notification and reply_markup and protected_cont ) assert check_shortcut_signature( Message.copy, Bot.copy_message, ["from_chat_id", "message_id"], [] ) assert await check_shortcut_call(message.copy, message.get_bot(), "copy_message") assert await check_defaults_handling(message.copy, message.get_bot()) monkeypatch.setattr(message.get_bot(), "copy_message", make_assertion) assert await message.copy( 123456, disable_notification=disable_notification, protect_content=protected ) assert await message.copy( 123456, reply_markup=keyboard, disable_notification=disable_notification, protect_content=protected, ) assert not await message.copy(635241) @pytest.mark.parametrize(("disable_notification", "protected"), [(True, False), (False, True)]) async def test_reply_copy(self, monkeypatch, message, disable_notification, protected): keyboard = [[1, 2]] async def make_assertion(*_, **kwargs): chat_id = kwargs["from_chat_id"] == 123456 from_chat = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == 456789 notification = kwargs["disable_notification"] == disable_notification is_protected = kwargs["protect_content"] == protected if kwargs.get("reply_markup") is not None: reply_markup = kwargs["reply_markup"] is keyboard else: reply_markup = True return ( chat_id and from_chat and message_id and notification and reply_markup and is_protected ) assert check_shortcut_signature( Message.reply_copy, Bot.copy_message, ["chat_id", "reply_to_message_id", "business_connection_id"], ["quote", "do_quote", "reply_to_message_id"], annotation_overrides={"message_thread_id": (ODVInput[int], DEFAULT_NONE)}, ) assert await check_shortcut_call(message.copy, message.get_bot(), "copy_message") assert await check_defaults_handling(message.copy, message.get_bot()) monkeypatch.setattr(message.get_bot(), "copy_message", make_assertion) assert await message.reply_copy( 123456, 456789, disable_notification=disable_notification, protect_content=protected ) assert await message.reply_copy( 123456, 456789, reply_markup=keyboard, disable_notification=disable_notification, protect_content=protected, ) await self.check_quote_parsing( message, message.reply_copy, "copy_message", [123456, 456789], monkeypatch, ) await self.check_thread_id_parsing( message, message.reply_copy, "copy_message", [123456, 456789], monkeypatch, ) async def test_reply_paid_media(self, monkeypatch, message): async def make_assertion(*_, **kwargs): id_ = kwargs["chat_id"] == message.chat_id media = kwargs["media"][0].media == "media" star_count = kwargs["star_count"] == 5 return id_ and media and star_count assert check_shortcut_signature( Message.reply_paid_media, Bot.send_paid_media, ["chat_id", "reply_to_message_id", "business_connection_id"], ["do_quote", "reply_to_message_id"], ) assert await check_shortcut_call( message.reply_paid_media, message.get_bot(), "send_paid_media", skip_params=["reply_to_message_id"], shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling( message.reply_paid_media, message.get_bot(), no_default_kwargs={"message_thread_id"} ) monkeypatch.setattr(message.get_bot(), "send_paid_media", make_assertion) assert await message.reply_paid_media( star_count=5, media=[InputPaidMediaPhoto(media="media")] ) await self.check_quote_parsing( message, message.reply_paid_media, "send_paid_media", ["test", [InputPaidMediaPhoto(media="media")]], monkeypatch, ) async def test_edit_text(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id text = kwargs["text"] == "test" return chat_id and message_id and text assert check_shortcut_signature( Message.edit_text, Bot.edit_message_text, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.edit_text, message.get_bot(), "edit_message_text", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.edit_text, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_text", make_assertion) assert await message.edit_text(text="test") async def test_edit_caption(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id caption = kwargs["caption"] == "new caption" return chat_id and message_id and caption assert check_shortcut_signature( Message.edit_caption, Bot.edit_message_caption, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.edit_caption, message.get_bot(), "edit_message_caption", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.edit_caption, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_caption", make_assertion) assert await message.edit_caption(caption="new caption") async def test_edit_media(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id media = kwargs["media"] == "my_media" return chat_id and message_id and media assert check_shortcut_signature( Message.edit_media, Bot.edit_message_media, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.edit_media, message.get_bot(), "edit_message_media", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.edit_media, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_media", make_assertion) assert await message.edit_media("my_media") async def test_edit_reply_markup(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id reply_markup = kwargs["reply_markup"] == [["1", "2"]] return chat_id and message_id and reply_markup assert check_shortcut_signature( Message.edit_reply_markup, Bot.edit_message_reply_markup, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.edit_reply_markup, message.get_bot(), "edit_message_reply_markup", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.edit_reply_markup, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_reply_markup", make_assertion) assert await message.edit_reply_markup(reply_markup=[["1", "2"]]) async def test_edit_live_location(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id latitude = kwargs["latitude"] == 1 longitude = kwargs["longitude"] == 2 live = kwargs["live_period"] == 900 return chat_id and message_id and longitude and latitude and live assert check_shortcut_signature( Message.edit_live_location, Bot.edit_message_live_location, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.edit_live_location, message.get_bot(), "edit_message_live_location", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.edit_live_location, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_message_live_location", make_assertion) assert await message.edit_live_location(latitude=1, longitude=2, live_period=900) async def test_stop_live_location(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.stop_live_location, Bot.stop_message_live_location, ["chat_id", "message_id", "inline_message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.stop_live_location, message.get_bot(), "stop_message_live_location", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id", "business_connection_id"], ) assert await check_defaults_handling(message.stop_live_location, message.get_bot()) monkeypatch.setattr(message.get_bot(), "stop_message_live_location", make_assertion) assert await message.stop_live_location() async def test_set_game_score(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id user_id = kwargs["user_id"] == 1 score = kwargs["score"] == 2 return chat_id and message_id and user_id and score assert check_shortcut_signature( Message.set_game_score, Bot.set_game_score, ["chat_id", "message_id", "inline_message_id"], [], ) assert await check_shortcut_call( message.set_game_score, message.get_bot(), "set_game_score", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id"], ) assert await check_defaults_handling(message.set_game_score, message.get_bot()) monkeypatch.setattr(message.get_bot(), "set_game_score", make_assertion) assert await message.set_game_score(user_id=1, score=2) async def test_get_game_high_scores(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id user_id = kwargs["user_id"] == 1 return chat_id and message_id and user_id assert check_shortcut_signature( Message.get_game_high_scores, Bot.get_game_high_scores, ["chat_id", "message_id", "inline_message_id"], [], ) assert await check_shortcut_call( message.get_game_high_scores, message.get_bot(), "get_game_high_scores", skip_params=["inline_message_id"], shortcut_kwargs=["message_id", "chat_id"], ) assert await check_defaults_handling(message.get_game_high_scores, message.get_bot()) monkeypatch.setattr(message.get_bot(), "get_game_high_scores", make_assertion) assert await message.get_game_high_scores(user_id=1) async def test_delete(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.delete, Bot.delete_message, ["chat_id", "message_id"], [] ) assert await check_shortcut_call(message.delete, message.get_bot(), "delete_message") assert await check_defaults_handling(message.delete, message.get_bot()) monkeypatch.setattr(message.get_bot(), "delete_message", make_assertion) assert await message.delete() async def test_stop_poll(self, monkeypatch, message): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.stop_poll, Bot.stop_poll, ["chat_id", "message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.stop_poll, message.get_bot(), "stop_poll", shortcut_kwargs=["business_connection_id"], ) assert await check_defaults_handling(message.stop_poll, message.get_bot()) monkeypatch.setattr(message.get_bot(), "stop_poll", make_assertion) assert await message.stop_poll() async def test_pin(self, monkeypatch, message): async def make_assertion(*args, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.pin, Bot.pin_chat_message, ["chat_id", "message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.pin, message.get_bot(), "pin_chat_message", shortcut_kwargs=["chat_id", "message_id", "business_connection_id"], ) assert await check_defaults_handling(message.pin, message.get_bot()) monkeypatch.setattr(message.get_bot(), "pin_chat_message", make_assertion) assert await message.pin() async def test_unpin(self, monkeypatch, message): async def make_assertion(*args, **kwargs): chat_id = kwargs["chat_id"] == message.chat_id message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.unpin, Bot.unpin_chat_message, ["chat_id", "message_id", "business_connection_id"], [], ) assert await check_shortcut_call( message.unpin, message.get_bot(), "unpin_chat_message", shortcut_kwargs=["chat_id", "message_id", "business_connection_id"], ) assert await check_defaults_handling(message.unpin, message.get_bot()) monkeypatch.setattr(message.get_bot(), "unpin_chat_message", make_assertion) assert await message.unpin() @pytest.mark.parametrize( ("default_quote", "chat_type", "expected"), [ (False, Chat.PRIVATE, False), (None, Chat.PRIVATE, False), (True, Chat.PRIVATE, True), (False, Chat.GROUP, False), (None, Chat.GROUP, True), (True, Chat.GROUP, True), (False, Chat.SUPERGROUP, False), (None, Chat.SUPERGROUP, True), (True, Chat.SUPERGROUP, True), (False, Chat.CHANNEL, False), (None, Chat.CHANNEL, True), (True, Chat.CHANNEL, True), ], ) async def test_default_do_quote( self, offline_bot, message, default_quote, chat_type, expected, monkeypatch ): original_bot = message.get_bot() temp_bot = PytestExtBot(token=offline_bot.token, defaults=Defaults(do_quote=default_quote)) message.set_bot(temp_bot) async def make_assertion(*_, **kwargs): reply_parameters = kwargs.get("reply_parameters") or ReplyParameters(message_id=False) condition = reply_parameters.message_id == message.message_id return condition == expected monkeypatch.setattr(message.get_bot(), "send_message", make_assertion) try: message.chat.type = chat_type assert await message.reply_text("test") finally: message.set_bot(original_bot) async def test_edit_forum_topic(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["message_thread_id"] == message.message_thread_id and kwargs["name"] == "New Name" and kwargs["icon_custom_emoji_id"] == "12345" ) assert check_shortcut_signature( Message.edit_forum_topic, Bot.edit_forum_topic, ["chat_id", "message_thread_id"], [] ) assert await check_shortcut_call( message.edit_forum_topic, message.get_bot(), "edit_forum_topic", shortcut_kwargs=["chat_id", "message_thread_id"], ) assert await check_defaults_handling(message.edit_forum_topic, message.get_bot()) monkeypatch.setattr(message.get_bot(), "edit_forum_topic", make_assertion) assert await message.edit_forum_topic(name="New Name", icon_custom_emoji_id="12345") async def test_close_forum_topic(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["message_thread_id"] == message.message_thread_id ) assert check_shortcut_signature( Message.close_forum_topic, Bot.close_forum_topic, ["chat_id", "message_thread_id"], [] ) assert await check_shortcut_call( message.close_forum_topic, message.get_bot(), "close_forum_topic", shortcut_kwargs=["chat_id", "message_thread_id"], ) assert await check_defaults_handling(message.close_forum_topic, message.get_bot()) monkeypatch.setattr(message.get_bot(), "close_forum_topic", make_assertion) assert await message.close_forum_topic() async def test_reopen_forum_topic(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["message_thread_id"] == message.message_thread_id ) assert check_shortcut_signature( Message.reopen_forum_topic, Bot.reopen_forum_topic, ["chat_id", "message_thread_id"], [], ) assert await check_shortcut_call( message.reopen_forum_topic, message.get_bot(), "reopen_forum_topic", shortcut_kwargs=["chat_id", "message_thread_id"], ) assert await check_defaults_handling(message.reopen_forum_topic, message.get_bot()) monkeypatch.setattr(message.get_bot(), "reopen_forum_topic", make_assertion) assert await message.reopen_forum_topic() async def test_delete_forum_topic(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["message_thread_id"] == message.message_thread_id ) assert check_shortcut_signature( Message.delete_forum_topic, Bot.delete_forum_topic, ["chat_id", "message_thread_id"], [], ) assert await check_shortcut_call( message.delete_forum_topic, message.get_bot(), "delete_forum_topic", shortcut_kwargs=["chat_id", "message_thread_id"], ) assert await check_defaults_handling(message.delete_forum_topic, message.get_bot()) monkeypatch.setattr(message.get_bot(), "delete_forum_topic", make_assertion) assert await message.delete_forum_topic() async def test_unpin_all_forum_topic_messages(self, monkeypatch, message): async def make_assertion(*_, **kwargs): return ( kwargs["chat_id"] == message.chat_id and kwargs["message_thread_id"] == message.message_thread_id ) assert check_shortcut_signature( Message.unpin_all_forum_topic_messages, Bot.unpin_all_forum_topic_messages, ["chat_id", "message_thread_id"], [], ) assert await check_shortcut_call( message.unpin_all_forum_topic_messages, message.get_bot(), "unpin_all_forum_topic_messages", shortcut_kwargs=["chat_id", "message_thread_id"], ) assert await check_defaults_handling( message.unpin_all_forum_topic_messages, message.get_bot() ) monkeypatch.setattr(message.get_bot(), "unpin_all_forum_topic_messages", make_assertion) assert await message.unpin_all_forum_topic_messages() def test_attachement_successful_payment_deprecated(self, message, recwarn): message.successful_payment = "something" # kinda unnecessary to assert but one needs to call the function ofc so. Here we are. assert message.effective_attachment == "something" assert len(recwarn) == 1 assert ( "successful_payment will no longer be considered an attachment in future major " "versions" in str(recwarn[0].message) ) assert recwarn[0].category is PTBDeprecationWarning assert recwarn[0].filename == __file__