1#!/usr/bin/env python 2# pylint: disable=R0902,R0913 3# 4# A library that provides a Python interface to the Telegram Bot API 5# Copyright (C) 2015-2020 6# Leandro Toledo de Souza <devs@python-telegram-bot.org> 7# 8# This program is free software: you can redistribute it and/or modify 9# it under the terms of the GNU Lesser Public License as published by 10# the Free Software Foundation, either version 3 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU Lesser Public License for more details. 17# 18# You should have received a copy of the GNU Lesser Public License 19# along with this program. If not, see [http://www.gnu.org/licenses/]. 20"""This module contains an object that represents a Telegram Message.""" 21import datetime 22import sys 23from html import escape 24from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, ClassVar 25 26from telegram import ( 27 Animation, 28 Audio, 29 Chat, 30 Contact, 31 Dice, 32 Document, 33 Game, 34 InlineKeyboardMarkup, 35 Invoice, 36 Location, 37 MessageEntity, 38 ParseMode, 39 PassportData, 40 PhotoSize, 41 Poll, 42 Sticker, 43 SuccessfulPayment, 44 TelegramObject, 45 User, 46 Venue, 47 Video, 48 VideoNote, 49 Voice, 50 ProximityAlertTriggered, 51) 52from telegram.utils.helpers import escape_markdown, from_timestamp, to_timestamp 53from telegram.utils.types import JSONDict 54 55if TYPE_CHECKING: 56 from telegram import Bot, GameHighScore, InputMedia, MessageId 57 58_UNDEFINED = object() 59 60 61class Message(TelegramObject): 62 # fmt: off 63 """This object represents a message. 64 65 Objects of this class are comparable in terms of equality. Two objects of this class are 66 considered equal, if their :attr:`message_id` and :attr:`chat` are equal. 67 68 Note: 69 * In Python `from` is a reserved word, use `from_user` instead. 70 71 Attributes: 72 message_id (:obj:`int`): Unique message identifier inside this chat. 73 from_user (:class:`telegram.User`): Optional. Sender. 74 sender_chat (:class:`telegram.Chat`): Optional. Sender of the message, sent on behalf of a 75 chat. The channel itself for channel messages. The supergroup itself for messages from 76 anonymous group administrators. The linked channel for messages automatically forwarded 77 to the discussion group. 78 date (:class:`datetime.datetime`): Date the message was sent. 79 chat (:class:`telegram.Chat`): Conversation the message belongs to. 80 forward_from (:class:`telegram.User`): Optional. Sender of the original message. 81 forward_from_chat (:class:`telegram.Chat`): Optional. For messages forwarded from channels 82 or from anonymous administrators, information about the original sender chat. 83 forward_from_message_id (:obj:`int`): Optional. Identifier of the original message in the 84 channel. 85 forward_date (:class:`datetime.datetime`): Optional. Date the original message was sent. 86 reply_to_message (:class:`telegram.Message`): Optional. For replies, the original message. 87 Note that the Message object in this field will not contain further 88 ``reply_to_message`` fields even if it itself is a reply. 89 edit_date (:class:`datetime.datetime`): Optional. Date the message was last edited. 90 media_group_id (:obj:`str`): Optional. The unique identifier of a media message group this 91 message belongs to. 92 text (:obj:`str`): Optional. The actual UTF-8 text of the message. 93 entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities like 94 usernames, URLs, bot commands, etc. that appear in the text. See 95 :attr:`Message.parse_entity` and :attr:`parse_entities` methods for how to use 96 properly. 97 caption_entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities like 98 usernames, URLs, bot commands, etc. that appear in the caption. See 99 :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` methods for how 100 to use properly. 101 audio (:class:`telegram.Audio`): Optional. Information about the file. 102 document (:class:`telegram.Document`): Optional. Information about the file. 103 animation (:class:`telegram.Animation`) Optional. Information about the file. 104 For backward compatibility, when this field is set, the document field will also be 105 set. 106 game (:class:`telegram.Game`): Optional. Information about the game. 107 photo (List[:class:`telegram.PhotoSize`]): Optional. Available sizes of the photo. 108 sticker (:class:`telegram.Sticker`): Optional. Information about the sticker. 109 video (:class:`telegram.Video`): Optional. Information about the video. 110 voice (:class:`telegram.Voice`): Optional. Information about the file. 111 video_note (:class:`telegram.VideoNote`): Optional. Information about the video message. 112 new_chat_members (List[:class:`telegram.User`]): Optional. Information about new members to 113 the chat. (the bot itself may be one of these members). 114 caption (:obj:`str`): Optional. Caption for the document, photo or video, 0-1024 115 characters. 116 contact (:class:`telegram.Contact`): Optional. Information about the contact. 117 location (:class:`telegram.Location`): Optional. Information about the location. 118 venue (:class:`telegram.Venue`): Optional. Information about the venue. 119 left_chat_member (:class:`telegram.User`): Optional. Information about the user that left 120 the group. (this member may be the bot itself). 121 new_chat_title (:obj:`str`): Optional. A chat title was changed to this value. 122 new_chat_photo (List[:class:`telegram.PhotoSize`]): Optional. A chat photo was changed to 123 this value. 124 delete_chat_photo (:obj:`bool`): Optional. The chat photo was deleted. 125 group_chat_created (:obj:`bool`): Optional. The group has been created. 126 supergroup_chat_created (:obj:`bool`): Optional. The supergroup has been created. 127 channel_chat_created (:obj:`bool`): Optional. The channel has been created. 128 migrate_to_chat_id (:obj:`int`): Optional. The group has been migrated to a supergroup with 129 the specified identifier. 130 migrate_from_chat_id (:obj:`int`): Optional. The supergroup has been migrated from a group 131 with the specified identifier. 132 pinned_message (:class:`telegram.message`): Optional. Specified message was pinned. 133 invoice (:class:`telegram.Invoice`): Optional. Information about the invoice. 134 successful_payment (:class:`telegram.SuccessfulPayment`): Optional. Information about the 135 payment. 136 connected_website (:obj:`str`): Optional. The domain name of the website on which the user 137 has logged in. 138 forward_signature (:obj:`str`): Optional. Signature of the post author for messages 139 forwarded from channels. 140 forward_sender_name (:obj:`str`): Optional. Sender's name for messages forwarded from users 141 who disallow adding a link to their account in forwarded messages. 142 author_signature (:obj:`str`): Optional. Signature of the post author for messages in 143 channels, or the custom title of an anonymous group administrator. 144 passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data. 145 poll (:class:`telegram.Poll`): Optional. Message is a native poll, 146 information about the poll. 147 dice (:class:`telegram.Dice`): Optional. Message is a dice. 148 via_bot (:class:`telegram.User`): Optional. Bot through which the message was sent. 149 proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`): Optional. Service 150 message. A user in the chat triggered another user's proximity alert while sharing 151 Live Location. 152 reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached 153 to the message. 154 bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. 155 156 Args: 157 message_id (:obj:`int`): Unique message identifier inside this chat. 158 from_user (:class:`telegram.User`, optional): Sender, empty for messages sent 159 to channels. 160 sender_chat (:class:`telegram.Chat`, optional): Sender of the message, sent on behalf of a 161 chat. The channel itself for channel messages. The supergroup itself for messages from 162 anonymous group administrators. The linked channel for messages automatically forwarded 163 to the discussion group. 164 date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to 165 :class:`datetime.datetime`. 166 chat (:class:`telegram.Chat`): Conversation the message belongs to. 167 forward_from (:class:`telegram.User`, optional): For forwarded messages, sender of 168 the original message. 169 forward_from_chat (:class:`telegram.Chat`, optional): For messages forwarded from channels 170 or from anonymous administrators, information about the original sender chat. 171 forward_from_message_id (:obj:`int`, optional): For forwarded channel posts, identifier of 172 the original message in the channel. 173 forward_sender_name (:obj:`str`, optional): Sender's name for messages forwarded from users 174 who disallow adding a link to their account in forwarded messages. 175 forward_date (:class:`datetime.datetime`, optional): For forwarded messages, date the 176 original message was sent in Unix time. Converted to :class:`datetime.datetime`. 177 reply_to_message (:class:`telegram.Message`, optional): For replies, the original message. 178 edit_date (:class:`datetime.datetime`, optional): Date the message was last edited in Unix 179 time. Converted to :class:`datetime.datetime`. 180 media_group_id (:obj:`str`, optional): The unique identifier of a media message group this 181 message belongs to. 182 text (str, optional): For text messages, the actual UTF-8 text of the message, 0-4096 183 characters. Also found as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`. 184 entities (List[:class:`telegram.MessageEntity`], optional): For text messages, special 185 entities like usernames, URLs, bot commands, etc. that appear in the text. See 186 :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. 187 caption_entities (List[:class:`telegram.MessageEntity`]): Optional. For Messages with a 188 Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the 189 caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` 190 methods for how to use properly. 191 audio (:class:`telegram.Audio`, optional): Message is an audio file, information 192 about the file. 193 document (:class:`telegram.Document`, optional): Message is a general file, information 194 about the file. 195 animation (:class:`telegram.Animation`, optional): Message is an animation, information 196 about the animation. For backward compatibility, when this field is set, the document 197 field will also be set. 198 game (:class:`telegram.Game`, optional): Message is a game, information about the game. 199 photo (List[:class:`telegram.PhotoSize`], optional): Message is a photo, available 200 sizes of the photo. 201 sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information 202 about the sticker. 203 video (:class:`telegram.Video`, optional): Message is a video, information about the video. 204 voice (:class:`telegram.Voice`, optional): Message is a voice message, information about 205 the file. 206 video_note (:class:`telegram.VideoNote`, optional): Message is a video note, information 207 about the video message. 208 new_chat_members (List[:class:`telegram.User`], optional): New members that were added to 209 the group or supergroup and information about them (the bot itself may be one of these 210 members). 211 caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video 212 or voice, 0-1024 characters. 213 contact (:class:`telegram.Contact`, optional): Message is a shared contact, information 214 about the contact. 215 location (:class:`telegram.Location`, optional): Message is a shared location, information 216 about the location. 217 venue (:class:`telegram.Venue`, optional): Message is a venue, information about the venue. 218 For backward compatibility, when this field is set, the location field will also be 219 set. 220 left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, 221 information about them (this member may be the bot itself). 222 new_chat_title (:obj:`str`, optional): A chat title was changed to this value. 223 new_chat_photo (List[:class:`telegram.PhotoSize`], optional): A chat photo was changed to 224 this value. 225 delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. 226 group_chat_created (:obj:`bool`, optional): Service message: The group has been created. 227 supergroup_chat_created (:obj:`bool`, optional): Service message: The supergroup has been 228 created. This field can't be received in a message coming through updates, because bot 229 can't be a member of a supergroup when it is created. It can only be found in 230 :attr:`reply_to_message` if someone replies to a very first message in a directly 231 created supergroup. 232 channel_chat_created (:obj:`bool`, optional): Service message: The channel has been 233 created. This field can't be received in a message coming through updates, because bot 234 can't be a member of a channel when it is created. It can only be found in 235 :attr:`reply_to_message` if someone replies to a very first message in a channel. 236 migrate_to_chat_id (:obj:`int`, optional): The group has been migrated to a supergroup with 237 the specified identifier. This number may be greater than 32 bits and some programming 238 languages may have difficulty/silent defects in interpreting it. But it is smaller than 239 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing 240 this identifier. 241 migrate_from_chat_id (:obj:`int`, optional): The supergroup has been migrated from a group 242 with the specified identifier. This number may be greater than 32 bits and some 243 programming languages may have difficulty/silent defects in interpreting it. But it is 244 smaller than 52 bits, so a signed 64 bit integer or double-precision float type are 245 safe for storing this identifier. 246 pinned_message (:class:`telegram.Message`, optional): Specified message was pinned. Note 247 that the Message object in this field will not contain further :attr:`reply_to_message` 248 fields even if it is itself a reply. 249 invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment, 250 information about the invoice. 251 successful_payment (:class:`telegram.SuccessfulPayment`, optional): Message is a service 252 message about a successful payment, information about the payment. 253 connected_website (:obj:`str`, optional): The domain name of the website on which the user 254 has logged in. 255 forward_signature (:obj:`str`, optional): For messages forwarded from channels, signature 256 of the post author if present. 257 author_signature (:obj:`str`, optional): Signature of the post author for messages in 258 channels, or the custom title of an anonymous group administrator. 259 passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data. 260 poll (:class:`telegram.Poll`, optional): Message is a native poll, 261 information about the poll. 262 dice (:class:`telegram.Dice`, optional): Message is a dice with random value from 1 to 6. 263 via_bot (:class:`telegram.User`, optional): Message was sent through an inline bot. 264 proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`, optional): Service 265 message. A user in the chat triggered another user's proximity alert while sharing 266 Live Location. 267 reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached 268 to the message. ``login_url`` buttons are represented as ordinary url buttons. 269 bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. 270 271 """ 272 # fmt: on 273 274 _effective_attachment = _UNDEFINED 275 276 ATTACHMENT_TYPES: ClassVar[List[str]] = [ 277 'audio', 278 'game', 279 'animation', 280 'document', 281 'photo', 282 'sticker', 283 'video', 284 'voice', 285 'video_note', 286 'contact', 287 'location', 288 'venue', 289 'invoice', 290 'successful_payment', 291 ] 292 MESSAGE_TYPES: ClassVar[List[str]] = [ 293 'text', 294 'new_chat_members', 295 'left_chat_member', 296 'new_chat_title', 297 'new_chat_photo', 298 'delete_chat_photo', 299 'group_chat_created', 300 'supergroup_chat_created', 301 'channel_chat_created', 302 'migrate_to_chat_id', 303 'migrate_from_chat_id', 304 'pinned_message', 305 'poll', 306 'dice', 307 'passport_data', 308 'proximity_alert_triggered', 309 ] + ATTACHMENT_TYPES 310 311 def __init__( 312 self, 313 message_id: int, 314 date: datetime.datetime, 315 chat: Chat, 316 from_user: User = None, 317 forward_from: User = None, 318 forward_from_chat: Chat = None, 319 forward_from_message_id: int = None, 320 forward_date: datetime.datetime = None, 321 reply_to_message: 'Message' = None, 322 edit_date: datetime.datetime = None, 323 text: str = None, 324 entities: List[MessageEntity] = None, 325 caption_entities: List[MessageEntity] = None, 326 audio: Audio = None, 327 document: Document = None, 328 game: Game = None, 329 photo: List[PhotoSize] = None, 330 sticker: Sticker = None, 331 video: Video = None, 332 voice: Voice = None, 333 video_note: VideoNote = None, 334 new_chat_members: List[User] = None, 335 caption: str = None, 336 contact: Contact = None, 337 location: Location = None, 338 venue: Venue = None, 339 left_chat_member: User = None, 340 new_chat_title: str = None, 341 new_chat_photo: List[PhotoSize] = None, 342 delete_chat_photo: bool = False, 343 group_chat_created: bool = False, 344 supergroup_chat_created: bool = False, 345 channel_chat_created: bool = False, 346 migrate_to_chat_id: int = None, 347 migrate_from_chat_id: int = None, 348 pinned_message: 'Message' = None, 349 invoice: Invoice = None, 350 successful_payment: SuccessfulPayment = None, 351 forward_signature: str = None, 352 author_signature: str = None, 353 media_group_id: str = None, 354 connected_website: str = None, 355 animation: Animation = None, 356 passport_data: PassportData = None, 357 poll: Poll = None, 358 forward_sender_name: str = None, 359 reply_markup: InlineKeyboardMarkup = None, 360 bot: 'Bot' = None, 361 dice: Dice = None, 362 via_bot: User = None, 363 proximity_alert_triggered: ProximityAlertTriggered = None, 364 sender_chat: Chat = None, 365 **_kwargs: Any, 366 ): 367 # Required 368 self.message_id = int(message_id) 369 # Optionals 370 self.from_user = from_user 371 self.sender_chat = sender_chat 372 self.date = date 373 self.chat = chat 374 self.forward_from = forward_from 375 self.forward_from_chat = forward_from_chat 376 self.forward_date = forward_date 377 self.reply_to_message = reply_to_message 378 self.edit_date = edit_date 379 self.text = text 380 self.entities = entities or list() 381 self.caption_entities = caption_entities or list() 382 self.audio = audio 383 self.game = game 384 self.document = document 385 self.photo = photo or list() 386 self.sticker = sticker 387 self.video = video 388 self.voice = voice 389 self.video_note = video_note 390 self.caption = caption 391 self.contact = contact 392 self.location = location 393 self.venue = venue 394 self.new_chat_members = new_chat_members or list() 395 self.left_chat_member = left_chat_member 396 self.new_chat_title = new_chat_title 397 self.new_chat_photo = new_chat_photo or list() 398 self.delete_chat_photo = bool(delete_chat_photo) 399 self.group_chat_created = bool(group_chat_created) 400 self.supergroup_chat_created = bool(supergroup_chat_created) 401 self.migrate_to_chat_id = migrate_to_chat_id 402 self.migrate_from_chat_id = migrate_from_chat_id 403 self.channel_chat_created = bool(channel_chat_created) 404 self.pinned_message = pinned_message 405 self.forward_from_message_id = forward_from_message_id 406 self.invoice = invoice 407 self.successful_payment = successful_payment 408 self.connected_website = connected_website 409 self.forward_signature = forward_signature 410 self.forward_sender_name = forward_sender_name 411 self.author_signature = author_signature 412 self.media_group_id = media_group_id 413 self.animation = animation 414 self.passport_data = passport_data 415 self.poll = poll 416 self.dice = dice 417 self.via_bot = via_bot 418 self.proximity_alert_triggered = proximity_alert_triggered 419 self.reply_markup = reply_markup 420 self.bot = bot 421 422 self._id_attrs = (self.message_id, self.chat) 423 424 @property 425 def chat_id(self) -> int: 426 """:obj:`int`: Shortcut for :attr:`telegram.Chat.id` for :attr:`chat`.""" 427 return self.chat.id 428 429 @property 430 def link(self) -> Optional[str]: 431 """:obj:`str`: Convenience property. If the chat of the message is not 432 a private chat or normal group, returns a t.me link of the message.""" 433 if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]: 434 if self.chat.username: 435 to_link = self.chat.username 436 else: 437 # Get rid of leading -100 for supergroups 438 to_link = f"c/{str(self.chat.id)[4:]}" 439 return f"https://t.me/{to_link}/{self.message_id}" 440 return None 441 442 @classmethod 443 def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> 'Message': 444 data = cls.parse_data(data) 445 446 if not data: 447 return None 448 449 data['from_user'] = User.de_json(data.get('from'), bot) 450 data['sender_chat'] = Chat.de_json(data.get('sender_chat'), bot) 451 data['date'] = from_timestamp(data['date']) 452 data['chat'] = Chat.de_json(data.get('chat'), bot) 453 data['entities'] = MessageEntity.de_list(data.get('entities'), bot) 454 data['caption_entities'] = MessageEntity.de_list(data.get('caption_entities'), bot) 455 data['forward_from'] = User.de_json(data.get('forward_from'), bot) 456 data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot) 457 data['forward_date'] = from_timestamp(data.get('forward_date')) 458 data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot) 459 data['edit_date'] = from_timestamp(data.get('edit_date')) 460 data['audio'] = Audio.de_json(data.get('audio'), bot) 461 data['document'] = Document.de_json(data.get('document'), bot) 462 data['animation'] = Animation.de_json(data.get('animation'), bot) 463 data['game'] = Game.de_json(data.get('game'), bot) 464 data['photo'] = PhotoSize.de_list(data.get('photo'), bot) 465 data['sticker'] = Sticker.de_json(data.get('sticker'), bot) 466 data['video'] = Video.de_json(data.get('video'), bot) 467 data['voice'] = Voice.de_json(data.get('voice'), bot) 468 data['video_note'] = VideoNote.de_json(data.get('video_note'), bot) 469 data['contact'] = Contact.de_json(data.get('contact'), bot) 470 data['location'] = Location.de_json(data.get('location'), bot) 471 data['venue'] = Venue.de_json(data.get('venue'), bot) 472 data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot) 473 data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot) 474 data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot) 475 data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) 476 data['invoice'] = Invoice.de_json(data.get('invoice'), bot) 477 data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot) 478 data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot) 479 data['poll'] = Poll.de_json(data.get('poll'), bot) 480 data['dice'] = Dice.de_json(data.get('dice'), bot) 481 data['via_bot'] = User.de_json(data.get('via_bot'), bot) 482 data['proximity_alert_triggered'] = ProximityAlertTriggered.de_json( 483 data.get('proximity_alert_triggered'), bot 484 ) 485 data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot) 486 487 return cls(bot=bot, **data) 488 489 @property 490 def effective_attachment( 491 self, 492 ) -> Union[ 493 Contact, 494 Document, 495 Animation, 496 Game, 497 Invoice, 498 Location, 499 List[PhotoSize], 500 Sticker, 501 SuccessfulPayment, 502 Venue, 503 Video, 504 VideoNote, 505 Voice, 506 None, 507 ]: 508 """ 509 :class:`telegram.Audio` 510 or :class:`telegram.Contact` 511 or :class:`telegram.Document` 512 or :class:`telegram.Animation` 513 or :class:`telegram.Game` 514 or :class:`telegram.Invoice` 515 or :class:`telegram.Location` 516 or List[:class:`telegram.PhotoSize`] 517 or :class:`telegram.Sticker` 518 or :class:`telegram.SuccessfulPayment` 519 or :class:`telegram.Venue` 520 or :class:`telegram.Video` 521 or :class:`telegram.VideoNote` 522 or :class:`telegram.Voice`: The attachment that this message was sent with. May be 523 :obj:`None` if no attachment was sent. 524 525 """ 526 if self._effective_attachment is not _UNDEFINED: 527 return self._effective_attachment # type: ignore 528 529 for i in Message.ATTACHMENT_TYPES: 530 if getattr(self, i, None): 531 self._effective_attachment = getattr(self, i) 532 break 533 else: 534 self._effective_attachment = None 535 536 return self._effective_attachment # type: ignore 537 538 def __getitem__(self, item: str) -> Any: # pylint: disable=R1710 539 if item in self.__dict__.keys(): 540 return self.__dict__[item] 541 if item == 'chat_id': 542 return self.chat.id 543 544 def to_dict(self) -> JSONDict: 545 data = super().to_dict() 546 547 # Required 548 data['date'] = to_timestamp(self.date) 549 # Optionals 550 if self.forward_date: 551 data['forward_date'] = to_timestamp(self.forward_date) 552 if self.edit_date: 553 data['edit_date'] = to_timestamp(self.edit_date) 554 if self.photo: 555 data['photo'] = [p.to_dict() for p in self.photo] 556 if self.entities: 557 data['entities'] = [e.to_dict() for e in self.entities] 558 if self.caption_entities: 559 data['caption_entities'] = [e.to_dict() for e in self.caption_entities] 560 if self.new_chat_photo: 561 data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo] 562 if self.new_chat_members: 563 data['new_chat_members'] = [u.to_dict() for u in self.new_chat_members] 564 565 return data 566 567 def _quote(self, kwargs: JSONDict) -> None: 568 """Modify kwargs for replying with or without quoting.""" 569 if 'reply_to_message_id' in kwargs: 570 if 'quote' in kwargs: 571 del kwargs['quote'] 572 573 elif 'quote' in kwargs: 574 if kwargs['quote']: 575 kwargs['reply_to_message_id'] = self.message_id 576 577 del kwargs['quote'] 578 579 else: 580 if self.bot.defaults: 581 default_quote = self.bot.defaults.quote 582 else: 583 default_quote = None 584 if (default_quote is None and self.chat.type != Chat.PRIVATE) or default_quote: 585 kwargs['reply_to_message_id'] = self.message_id 586 587 def reply_text(self, *args: Any, **kwargs: Any) -> 'Message': 588 """Shortcut for:: 589 590 bot.send_message(update.message.chat_id, *args, **kwargs) 591 592 Keyword Args: 593 quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual 594 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 595 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 596 private chats. 597 598 Returns: 599 :class:`telegram.Message`: On success, instance representing the message posted. 600 601 """ 602 self._quote(kwargs) 603 return self.bot.send_message(self.chat_id, *args, **kwargs) 604 605 def reply_markdown(self, *args: Any, **kwargs: Any) -> 'Message': 606 """Shortcut for:: 607 608 bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN, *args, 609 **kwargs) 610 611 Sends a message with Markdown version 1 formatting. 612 613 Note: 614 :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for 615 backward compatibility. You should use :meth:`reply_markdown_v2` instead. 616 617 Keyword Args: 618 quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual 619 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 620 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 621 private chats. 622 623 Returns: 624 :class:`telegram.Message`: On success, instance representing the message posted. 625 """ 626 627 kwargs['parse_mode'] = ParseMode.MARKDOWN 628 629 self._quote(kwargs) 630 631 return self.bot.send_message(self.chat_id, *args, **kwargs) 632 633 def reply_markdown_v2(self, *args: Any, **kwargs: Any) -> 'Message': 634 """Shortcut for:: 635 636 bot.send_message(update.message.chat_id, parse_mode=ParseMode.MARKDOWN_V2, *args, 637 **kwargs) 638 639 Sends a message with markdown version 2 formatting. 640 641 Keyword Args: 642 quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual 643 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 644 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 645 private chats. 646 647 Returns: 648 :class:`telegram.Message`: On success, instance representing the message posted. 649 """ 650 651 kwargs['parse_mode'] = ParseMode.MARKDOWN_V2 652 653 self._quote(kwargs) 654 655 return self.bot.send_message(self.chat_id, *args, **kwargs) 656 657 def reply_html(self, *args: Any, **kwargs: Any) -> 'Message': 658 """Shortcut for:: 659 660 bot.send_message(update.message.chat_id, parse_mode=ParseMode.HTML, *args, **kwargs) 661 662 Sends a message with HTML formatting. 663 664 Keyword Args: 665 quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual 666 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 667 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 668 private chats. 669 670 Returns: 671 :class:`telegram.Message`: On success, instance representing the message posted. 672 """ 673 674 kwargs['parse_mode'] = ParseMode.HTML 675 676 self._quote(kwargs) 677 678 return self.bot.send_message(self.chat_id, *args, **kwargs) 679 680 def reply_media_group(self, *args: Any, **kwargs: Any) -> List[Optional['Message']]: 681 """Shortcut for:: 682 683 bot.send_media_group(update.message.chat_id, *args, **kwargs) 684 685 Keyword Args: 686 quote (:obj:`bool`, optional): If set to :obj:`True`, the media group is sent as an 687 actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 688 this parameter will be ignored. Default: :obj:`True` in group chats and 689 :obj:`False` in private chats. 690 691 Returns: 692 List[:class:`telegram.Message`]: An array of the sent Messages. 693 694 Raises: 695 :class:`telegram.TelegramError` 696 """ 697 self._quote(kwargs) 698 return self.bot.send_media_group(self.chat_id, *args, **kwargs) 699 700 def reply_photo(self, *args: Any, **kwargs: Any) -> 'Message': 701 """Shortcut for:: 702 703 bot.send_photo(update.message.chat_id, *args, **kwargs) 704 705 Keyword Args: 706 quote (:obj:`bool`, optional): If set to :obj:`True`, the photo is sent as an actual 707 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 708 this parameter will be ignored. Default: :obj:`True` in group chats and 709 :obj:`False` in private chats. 710 711 Returns: 712 :class:`telegram.Message`: On success, instance representing the message posted. 713 714 """ 715 self._quote(kwargs) 716 return self.bot.send_photo(self.chat_id, *args, **kwargs) 717 718 def reply_audio(self, *args: Any, **kwargs: Any) -> 'Message': 719 """Shortcut for:: 720 721 bot.send_audio(update.message.chat_id, *args, **kwargs) 722 723 Keyword Args: 724 quote (:obj:`bool`, optional): If set to :obj:`True`, the audio is sent as an actual 725 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 726 this parameter will be ignored. Default: :obj:`True` in group chats and 727 :obj:`False` in private chats. 728 729 Returns: 730 :class:`telegram.Message`: On success, instance representing the message posted. 731 732 """ 733 self._quote(kwargs) 734 return self.bot.send_audio(self.chat_id, *args, **kwargs) 735 736 def reply_document(self, *args: Any, **kwargs: Any) -> 'Message': 737 """Shortcut for:: 738 739 bot.send_document(update.message.chat_id, *args, **kwargs) 740 741 Keyword Args: 742 quote (:obj:`bool`, optional): If set to :obj:`True`, the document is sent as an actual 743 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 744 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 745 private chats. 746 747 Returns: 748 :class:`telegram.Message`: On success, instance representing the message posted. 749 750 """ 751 self._quote(kwargs) 752 return self.bot.send_document(self.chat_id, *args, **kwargs) 753 754 def reply_animation(self, *args: Any, **kwargs: Any) -> 'Message': 755 """Shortcut for:: 756 757 bot.send_animation(update.message.chat_id, *args, **kwargs) 758 759 Keyword Args: 760 quote (:obj:`bool`, optional): If set to :obj:`True`, the animation is sent as an 761 actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 762 this parameter will be ignored. Default: :obj:`True` in group chats and 763 :obj:`False` in private chats. 764 765 Returns: 766 :class:`telegram.Message`: On success, instance representing the message posted. 767 768 """ 769 self._quote(kwargs) 770 return self.bot.send_animation(self.chat_id, *args, **kwargs) 771 772 def reply_sticker(self, *args: Any, **kwargs: Any) -> 'Message': 773 """Shortcut for:: 774 775 bot.send_sticker(update.message.chat_id, *args, **kwargs) 776 777 Keyword Args: 778 quote (:obj:`bool`, optional): If set to :obj:`True`, the sticker is sent as an actual 779 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 780 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 781 private chats. 782 783 Returns: 784 :class:`telegram.Message`: On success, instance representing the message posted. 785 786 """ 787 self._quote(kwargs) 788 return self.bot.send_sticker(self.chat_id, *args, **kwargs) 789 790 def reply_video(self, *args: Any, **kwargs: Any) -> 'Message': 791 """Shortcut for:: 792 793 bot.send_video(update.message.chat_id, *args, **kwargs) 794 795 Keyword Args: 796 quote (:obj:`bool`, optional): If set to :obj:`True`, the video is sent as an actual 797 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 798 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 799 private chats. 800 801 Returns: 802 :class:`telegram.Message`: On success, instance representing the message posted. 803 804 """ 805 self._quote(kwargs) 806 return self.bot.send_video(self.chat_id, *args, **kwargs) 807 808 def reply_video_note(self, *args: Any, **kwargs: Any) -> 'Message': 809 """Shortcut for:: 810 811 bot.send_video_note(update.message.chat_id, *args, **kwargs) 812 813 Keyword Args: 814 quote (:obj:`bool`, optional): If set to :obj:`True`, the video note is sent as an 815 actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 816 this parameter will be ignored. Default: :obj:`True` in group chats and 817 :obj:`False` in private chats. 818 819 Returns: 820 :class:`telegram.Message`: On success, instance representing the message posted. 821 822 """ 823 self._quote(kwargs) 824 return self.bot.send_video_note(self.chat_id, *args, **kwargs) 825 826 def reply_voice(self, *args: Any, **kwargs: Any) -> 'Message': 827 """Shortcut for:: 828 829 bot.send_voice(update.message.chat_id, *args, **kwargs) 830 831 Keyword Args: 832 quote (:obj:`bool`, optional): If set to :obj:`True`, the voice note is sent as an 833 actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 834 this parameter will be ignored. Default: :obj:`True` in group chats and 835 :obj:`False` in private chats. 836 837 Returns: 838 :class:`telegram.Message`: On success, instance representing the message posted. 839 840 """ 841 self._quote(kwargs) 842 return self.bot.send_voice(self.chat_id, *args, **kwargs) 843 844 def reply_location(self, *args: Any, **kwargs: Any) -> 'Message': 845 """Shortcut for:: 846 847 bot.send_location(update.message.chat_id, *args, **kwargs) 848 849 Keyword Args: 850 quote (:obj:`bool`, optional): If set to :obj:`True`, the location is sent as an actual 851 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 852 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 853 private chats. 854 855 Returns: 856 :class:`telegram.Message`: On success, instance representing the message posted. 857 858 """ 859 self._quote(kwargs) 860 return self.bot.send_location(self.chat_id, *args, **kwargs) 861 862 def reply_venue(self, *args: Any, **kwargs: Any) -> 'Message': 863 """Shortcut for:: 864 865 bot.send_venue(update.message.chat_id, *args, **kwargs) 866 867 Keyword Args: 868 quote (:obj:`bool`, optional): If set to :obj:`True`, the venue is sent as an actual 869 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 870 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 871 private chats. 872 873 Returns: 874 :class:`telegram.Message`: On success, instance representing the message posted. 875 876 """ 877 self._quote(kwargs) 878 return self.bot.send_venue(self.chat_id, *args, **kwargs) 879 880 def reply_contact(self, *args: Any, **kwargs: Any) -> 'Message': 881 """Shortcut for:: 882 883 bot.send_contact(update.message.chat_id, *args, **kwargs) 884 885 Keyword Args: 886 quote (:obj:`bool`, optional): If set to :obj:`True`, the contact is sent as an actual 887 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 888 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in 889 private chats. 890 891 Returns: 892 :class:`telegram.Message`: On success, instance representing the message posted. 893 894 """ 895 self._quote(kwargs) 896 return self.bot.send_contact(self.chat_id, *args, **kwargs) 897 898 def reply_poll(self, *args: Any, **kwargs: Any) -> 'Message': 899 """Shortcut for:: 900 901 bot.send_poll(update.message.chat_id, *args, **kwargs) 902 903 Keyword Args: 904 quote (:obj:`bool`, optional): If set to :obj:`True`, the poll is sent as an actual 905 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, 906 this parameter will be ignored. Default: :obj:`True` in group chats and 907 :obj:`False` in private chats. 908 909 Returns: 910 :class:`telegram.Message`: On success, instance representing the message posted. 911 912 """ 913 self._quote(kwargs) 914 return self.bot.send_poll(self.chat_id, *args, **kwargs) 915 916 def reply_dice(self, *args: Any, **kwargs: Any) -> 'Message': 917 """Shortcut for:: 918 919 bot.send_dice(update.message.chat_id, *args, **kwargs) 920 921 Keyword Args: 922 quote (:obj:`bool`, optional): If set to :obj:`True`, the dice is sent as an actual 923 reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this 924 parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` 925 in private chats. 926 927 Returns: 928 :class:`telegram.Message`: On success, instance representing the message posted. 929 930 """ 931 self._quote(kwargs) 932 return self.bot.send_dice(self.chat_id, *args, **kwargs) 933 934 def forward(self, chat_id: int, *args: Any, **kwargs: Any) -> 'Message': 935 """Shortcut for:: 936 937 bot.forward_message(chat_id=chat_id, 938 from_chat_id=update.message.chat_id, 939 message_id=update.message.message_id, 940 *args, 941 **kwargs) 942 943 Returns: 944 :class:`telegram.Message`: On success, instance representing the message forwarded. 945 946 """ 947 return self.bot.forward_message( 948 chat_id=chat_id, from_chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 949 ) 950 951 def copy(self, chat_id: int, *args: Any, **kwargs: Any) -> 'MessageId': 952 """Shortcut for:: 953 954 bot.copy_message(chat_id=chat_id, 955 from_chat_id=update.message.chat_id, 956 message_id=update.message.message_id, 957 *args, 958 **kwargs) 959 960 Returns: 961 :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. 962 963 """ 964 return self.bot.copy_message( 965 chat_id=chat_id, from_chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 966 ) 967 968 def reply_copy( 969 self, from_chat_id: int, message_id: int, *args: Any, **kwargs: Any 970 ) -> 'MessageId': 971 """Shortcut for:: 972 973 bot.copy_message(chat_id=message.chat.id, 974 from_chat_id=from_chat_id, 975 message_id=message_id, 976 *args, 977 **kwargs) 978 979 Returns: 980 :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. 981 982 """ 983 return self.bot.copy_message( 984 chat_id=self.chat_id, from_chat_id=from_chat_id, message_id=message_id, *args, **kwargs 985 ) 986 987 def edit_text(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 988 """Shortcut for:: 989 990 bot.edit_message_text(chat_id=message.chat_id, 991 message_id=message.message_id, 992 *args, 993 **kwargs) 994 995 Note: 996 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 997 of methods) or channel posts, if the bot is an admin in that channel. However, this 998 behaviour is undocumented and might be changed by Telegram. 999 1000 Returns: 1001 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1002 edited Message is returned, otherwise ``True`` is returned. 1003 1004 """ 1005 return self.bot.edit_message_text( 1006 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1007 ) 1008 1009 def edit_caption(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 1010 """Shortcut for:: 1011 1012 bot.edit_message_caption(chat_id=message.chat_id, 1013 message_id=message.message_id, 1014 *args, 1015 **kwargs) 1016 1017 Note: 1018 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1019 of methods) or channel posts, if the bot is an admin in that channel. However, this 1020 behaviour is undocumented and might be changed by Telegram. 1021 1022 Returns: 1023 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1024 edited Message is returned, otherwise ``True`` is returned. 1025 1026 """ 1027 return self.bot.edit_message_caption( 1028 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1029 ) 1030 1031 def edit_media(self, media: 'InputMedia', *args: Any, **kwargs: Any) -> Union['Message', bool]: 1032 """Shortcut for:: 1033 1034 bot.edit_message_media(chat_id=message.chat_id, 1035 message_id=message.message_id, 1036 *args, 1037 **kwargs) 1038 1039 Note: 1040 You can only edit messages that the bot sent itself(i.e. of the ``bot.send_*`` family 1041 of methods) or channel posts, if the bot is an admin in that channel. However, this 1042 behaviour is undocumented and might be changed by Telegram. 1043 1044 Returns: 1045 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1046 edited Message is returned, otherwise ``True`` is returned. 1047 1048 """ 1049 return self.bot.edit_message_media( 1050 chat_id=self.chat_id, message_id=self.message_id, media=media, *args, **kwargs 1051 ) 1052 1053 def edit_reply_markup(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 1054 """Shortcut for:: 1055 1056 bot.edit_message_reply_markup(chat_id=message.chat_id, 1057 message_id=message.message_id, 1058 *args, 1059 **kwargs) 1060 1061 Note: 1062 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1063 of methods) or channel posts, if the bot is an admin in that channel. However, this 1064 behaviour is undocumented and might be changed by Telegram. 1065 1066 Returns: 1067 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1068 edited Message is returned, otherwise ``True`` is returned. 1069 """ 1070 return self.bot.edit_message_reply_markup( 1071 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1072 ) 1073 1074 def edit_live_location(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 1075 """Shortcut for:: 1076 1077 bot.edit_message_live_location(chat_id=message.chat_id, 1078 message_id=message.message_id, 1079 *args, 1080 **kwargs) 1081 1082 Note: 1083 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1084 of methods) or channel posts, if the bot is an admin in that channel. However, this 1085 behaviour is undocumented and might be changed by Telegram. 1086 1087 Returns: 1088 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1089 edited Message is returned, otherwise :obj:`True` is returned. 1090 """ 1091 return self.bot.edit_message_live_location( 1092 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1093 ) 1094 1095 def stop_live_location(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 1096 """Shortcut for:: 1097 1098 bot.stop_message_live_location(chat_id=message.chat_id, 1099 message_id=message.message_id, 1100 *args, 1101 **kwargs) 1102 1103 Note: 1104 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1105 of methods) or channel posts, if the bot is an admin in that channel. However, this 1106 behaviour is undocumented and might be changed by Telegram. 1107 1108 Returns: 1109 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1110 edited Message is returned, otherwise :obj:`True` is returned. 1111 """ 1112 return self.bot.stop_message_live_location( 1113 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1114 ) 1115 1116 def set_game_score(self, *args: Any, **kwargs: Any) -> Union['Message', bool]: 1117 """Shortcut for:: 1118 1119 bot.set_game_score(chat_id=message.chat_id, 1120 message_id=message.message_id, 1121 *args, 1122 **kwargs) 1123 1124 Note: 1125 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1126 of methods) or channel posts, if the bot is an admin in that channel. However, this 1127 behaviour is undocumented and might be changed by Telegram. 1128 1129 Returns: 1130 :class:`telegram.Message`: On success, if edited message is sent by the bot, the 1131 edited Message is returned, otherwise :obj:`True` is returned. 1132 """ 1133 return self.bot.set_game_score( 1134 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1135 ) 1136 1137 def get_game_high_scores(self, *args: Any, **kwargs: Any) -> List['GameHighScore']: 1138 """Shortcut for:: 1139 1140 bot.get_game_high_scores(chat_id=message.chat_id, 1141 message_id=message.message_id, 1142 *args, 1143 **kwargs) 1144 1145 Note: 1146 You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family 1147 of methods) or channel posts, if the bot is an admin in that channel. However, this 1148 behaviour is undocumented and might be changed by Telegram. 1149 1150 Returns: 1151 List[:class:`telegram.GameHighScore`] 1152 """ 1153 return self.bot.get_game_high_scores( 1154 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1155 ) 1156 1157 def delete(self, *args: Any, **kwargs: Any) -> bool: 1158 """Shortcut for:: 1159 1160 bot.delete_message(chat_id=message.chat_id, 1161 message_id=message.message_id, 1162 *args, 1163 **kwargs) 1164 1165 Returns: 1166 :obj:`bool`: On success, :obj:`True` is returned. 1167 1168 """ 1169 return self.bot.delete_message( 1170 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1171 ) 1172 1173 def stop_poll(self, *args: Any, **kwargs: Any) -> Poll: 1174 """Shortcut for:: 1175 1176 bot.stop_poll(chat_id=message.chat_id, 1177 message_id=message.message_id, 1178 *args, 1179 **kwargs) 1180 1181 Returns: 1182 :class:`telegram.Poll`: On success, the stopped Poll with the final results is 1183 returned. 1184 1185 """ 1186 return self.bot.stop_poll( 1187 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1188 ) 1189 1190 def pin(self, *args: Any, **kwargs: Any) -> bool: 1191 """Shortcut for:: 1192 1193 bot.pin_chat_message(chat_id=message.chat_id, 1194 message_id=message.message_id, 1195 *args, 1196 **kwargs) 1197 1198 Returns: 1199 :obj:`bool`: On success, :obj:`True` is returned. 1200 1201 """ 1202 return self.bot.pin_chat_message( 1203 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1204 ) 1205 1206 def unpin(self, *args: Any, **kwargs: Any) -> bool: 1207 """Shortcut for:: 1208 1209 bot.unpin_chat_message(chat_id=message.chat_id, 1210 message_id=message.message_id, 1211 *args, 1212 **kwargs) 1213 1214 Returns: 1215 :obj:`bool`: On success, :obj:`True` is returned. 1216 1217 """ 1218 return self.bot.unpin_chat_message( 1219 chat_id=self.chat_id, message_id=self.message_id, *args, **kwargs 1220 ) 1221 1222 def parse_entity(self, entity: MessageEntity) -> str: 1223 """Returns the text from a given :class:`telegram.MessageEntity`. 1224 1225 Note: 1226 This method is present because Telegram calculates the offset and length in 1227 UTF-16 codepoint pairs, which some versions of Python don't handle automatically. 1228 (That is, you can't just slice ``Message.text`` with the offset and length.) 1229 1230 Args: 1231 entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must 1232 be an entity that belongs to this message. 1233 1234 Returns: 1235 :obj:`str`: The text of the given entity. 1236 1237 Raises: 1238 RuntimeError: If the message has no text. 1239 1240 """ 1241 if not self.text: 1242 raise RuntimeError("This Message has no 'text'.") 1243 1244 # Is it a narrow build, if so we don't need to convert 1245 if sys.maxunicode == 0xFFFF: 1246 return self.text[entity.offset : entity.offset + entity.length] 1247 1248 entity_text = self.text.encode('utf-16-le') 1249 entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] 1250 return entity_text.decode('utf-16-le') 1251 1252 def parse_caption_entity(self, entity: MessageEntity) -> str: 1253 """Returns the text from a given :class:`telegram.MessageEntity`. 1254 1255 Note: 1256 This method is present because Telegram calculates the offset and length in 1257 UTF-16 codepoint pairs, which some versions of Python don't handle automatically. 1258 (That is, you can't just slice ``Message.caption`` with the offset and length.) 1259 1260 Args: 1261 entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must 1262 be an entity that belongs to this message. 1263 1264 Returns: 1265 :obj:`str`: The text of the given entity. 1266 1267 Raises: 1268 RuntimeError: If the message has no caption. 1269 1270 """ 1271 if not self.caption: 1272 raise RuntimeError("This Message has no 'caption'.") 1273 1274 # Is it a narrow build, if so we don't need to convert 1275 if sys.maxunicode == 0xFFFF: 1276 return self.caption[entity.offset : entity.offset + entity.length] 1277 1278 entity_text = self.caption.encode('utf-16-le') 1279 entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] 1280 return entity_text.decode('utf-16-le') 1281 1282 def parse_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: 1283 """ 1284 Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. 1285 It contains entities from this message filtered by their 1286 :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity 1287 belongs to as the value of the :obj:`dict`. 1288 1289 Note: 1290 This method should always be used instead of the :attr:`entities` attribute, since it 1291 calculates the correct substring from the message text based on UTF-16 codepoints. 1292 See :attr:`parse_entity` for more info. 1293 1294 Args: 1295 types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as 1296 strings. If the ``type`` attribute of an entity is contained in this list, it will 1297 be returned. Defaults to a list of all types. All types can be found as constants 1298 in :class:`telegram.MessageEntity`. 1299 1300 Returns: 1301 Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to 1302 the text that belongs to them, calculated based on UTF-16 codepoints. 1303 1304 """ 1305 if types is None: 1306 types = MessageEntity.ALL_TYPES 1307 1308 return { 1309 entity: self.parse_entity(entity) 1310 for entity in (self.entities or []) 1311 if entity.type in types 1312 } 1313 1314 def parse_caption_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: 1315 """ 1316 Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. 1317 It contains entities from this message's caption filtered by their 1318 :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity 1319 belongs to as the value of the :obj:`dict`. 1320 1321 Note: 1322 This method should always be used instead of the :attr:`caption_entities` attribute, 1323 since it calculates the correct substring from the message text based on UTF-16 1324 codepoints. See :attr:`parse_entity` for more info. 1325 1326 Args: 1327 types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as 1328 strings. If the ``type`` attribute of an entity is contained in this list, it will 1329 be returned. Defaults to a list of all types. All types can be found as constants 1330 in :class:`telegram.MessageEntity`. 1331 1332 Returns: 1333 Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to 1334 the text that belongs to them, calculated based on UTF-16 codepoints. 1335 1336 """ 1337 if types is None: 1338 types = MessageEntity.ALL_TYPES 1339 1340 return { 1341 entity: self.parse_caption_entity(entity) 1342 for entity in (self.caption_entities or []) 1343 if entity.type in types 1344 } 1345 1346 @staticmethod 1347 def _parse_html( 1348 message_text: Optional[str], 1349 entities: Dict[MessageEntity, str], 1350 urled: bool = False, 1351 offset: int = 0, 1352 ) -> Optional[str]: 1353 if message_text is None: 1354 return None 1355 1356 if sys.maxunicode != 0xFFFF: 1357 message_text = message_text.encode('utf-16-le') # type: ignore 1358 1359 html_text = '' 1360 last_offset = 0 1361 1362 sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) 1363 parsed_entities = [] 1364 1365 for (entity, text) in sorted_entities: 1366 if entity not in parsed_entities: 1367 nested_entities = { 1368 e: t 1369 for (e, t) in sorted_entities 1370 if e.offset >= entity.offset 1371 and e.offset + e.length <= entity.offset + entity.length 1372 and e != entity 1373 } 1374 parsed_entities.extend(list(nested_entities.keys())) 1375 1376 text = escape(text) 1377 1378 if nested_entities: 1379 text = Message._parse_html( 1380 text, nested_entities, urled=urled, offset=entity.offset 1381 ) 1382 1383 if entity.type == MessageEntity.TEXT_LINK: 1384 insert = f'<a href="{entity.url}">{text}</a>' 1385 elif entity.type == MessageEntity.TEXT_MENTION and entity.user: 1386 insert = f'<a href="tg://user?id={entity.user.id}">{text}</a>' 1387 elif entity.type == MessageEntity.URL and urled: 1388 insert = f'<a href="{text}">{text}</a>' 1389 elif entity.type == MessageEntity.BOLD: 1390 insert = '<b>' + text + '</b>' 1391 elif entity.type == MessageEntity.ITALIC: 1392 insert = '<i>' + text + '</i>' 1393 elif entity.type == MessageEntity.CODE: 1394 insert = '<code>' + text + '</code>' 1395 elif entity.type == MessageEntity.PRE: 1396 if entity.language: 1397 insert = f'<pre><code class="{entity.language}">{text}</code></pre>' 1398 else: 1399 insert = '<pre>' + text + '</pre>' 1400 elif entity.type == MessageEntity.UNDERLINE: 1401 insert = '<u>' + text + '</u>' 1402 elif entity.type == MessageEntity.STRIKETHROUGH: 1403 insert = '<s>' + text + '</s>' 1404 else: 1405 insert = text 1406 1407 if offset == 0: 1408 if sys.maxunicode == 0xFFFF: 1409 html_text += ( 1410 escape(message_text[last_offset : entity.offset - offset]) + insert 1411 ) 1412 else: 1413 html_text += ( 1414 escape( 1415 message_text[ # type: ignore 1416 last_offset * 2 : (entity.offset - offset) * 2 1417 ].decode('utf-16-le') 1418 ) 1419 + insert 1420 ) 1421 else: 1422 if sys.maxunicode == 0xFFFF: 1423 html_text += message_text[last_offset : entity.offset - offset] + insert 1424 else: 1425 html_text += ( 1426 message_text[ # type: ignore 1427 last_offset * 2 : (entity.offset - offset) * 2 1428 ].decode('utf-16-le') 1429 + insert 1430 ) 1431 1432 last_offset = entity.offset - offset + entity.length 1433 1434 if offset == 0: 1435 if sys.maxunicode == 0xFFFF: 1436 html_text += escape(message_text[last_offset:]) 1437 else: 1438 html_text += escape( 1439 message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore 1440 ) 1441 else: 1442 if sys.maxunicode == 0xFFFF: 1443 html_text += message_text[last_offset:] 1444 else: 1445 html_text += message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore 1446 1447 return html_text 1448 1449 @property 1450 def text_html(self) -> str: 1451 """Creates an HTML-formatted string from the markup entities found in the message. 1452 1453 Use this if you want to retrieve the message text with the entities formatted as HTML in 1454 the same way the original message was formatted. 1455 1456 Returns: 1457 :obj:`str`: Message text with entities formatted as HTML. 1458 1459 """ 1460 return self._parse_html(self.text, self.parse_entities(), urled=False) 1461 1462 @property 1463 def text_html_urled(self) -> str: 1464 """Creates an HTML-formatted string from the markup entities found in the message. 1465 1466 Use this if you want to retrieve the message text with the entities formatted as HTML. 1467 This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1468 1469 Returns: 1470 :obj:`str`: Message text with entities formatted as HTML. 1471 1472 """ 1473 return self._parse_html(self.text, self.parse_entities(), urled=True) 1474 1475 @property 1476 def caption_html(self) -> str: 1477 """Creates an HTML-formatted string from the markup entities found in the message's 1478 caption. 1479 1480 Use this if you want to retrieve the message caption with the caption entities formatted as 1481 HTML in the same way the original message was formatted. 1482 1483 Returns: 1484 :obj:`str`: Message caption with caption entities formatted as HTML. 1485 1486 """ 1487 return self._parse_html(self.caption, self.parse_caption_entities(), urled=False) 1488 1489 @property 1490 def caption_html_urled(self) -> str: 1491 """Creates an HTML-formatted string from the markup entities found in the message's 1492 caption. 1493 1494 Use this if you want to retrieve the message caption with the caption entities formatted as 1495 HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1496 1497 Returns: 1498 :obj:`str`: Message caption with caption entities formatted as HTML. 1499 1500 """ 1501 return self._parse_html(self.caption, self.parse_caption_entities(), urled=True) 1502 1503 @staticmethod 1504 def _parse_markdown( 1505 message_text: Optional[str], 1506 entities: Dict[MessageEntity, str], 1507 urled: bool = False, 1508 version: int = 1, 1509 offset: int = 0, 1510 ) -> Optional[str]: 1511 version = int(version) 1512 1513 if message_text is None: 1514 return None 1515 1516 if sys.maxunicode != 0xFFFF: 1517 message_text = message_text.encode('utf-16-le') # type: ignore 1518 1519 markdown_text = '' 1520 last_offset = 0 1521 1522 sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) 1523 parsed_entities = [] 1524 1525 for (entity, text) in sorted_entities: 1526 if entity not in parsed_entities: 1527 nested_entities = { 1528 e: t 1529 for (e, t) in sorted_entities 1530 if e.offset >= entity.offset 1531 and e.offset + e.length <= entity.offset + entity.length 1532 and e != entity 1533 } 1534 parsed_entities.extend(list(nested_entities.keys())) 1535 1536 orig_text = text 1537 text = escape_markdown(text, version=version) 1538 1539 if nested_entities: 1540 if version < 2: 1541 raise ValueError( 1542 'Nested entities are not supported for Markdown ' 'version 1' 1543 ) 1544 1545 text = Message._parse_markdown( 1546 text, nested_entities, urled=urled, offset=entity.offset, version=version 1547 ) 1548 1549 if entity.type == MessageEntity.TEXT_LINK: 1550 if version == 1: 1551 url = entity.url 1552 else: 1553 # Links need special escaping. Also can't have entities nested within 1554 url = escape_markdown( 1555 entity.url, version=version, entity_type=MessageEntity.TEXT_LINK 1556 ) 1557 insert = f'[{text}]({url})' 1558 elif entity.type == MessageEntity.TEXT_MENTION and entity.user: 1559 insert = f'[{text}](tg://user?id={entity.user.id})' 1560 elif entity.type == MessageEntity.URL and urled: 1561 if version == 1: 1562 link = orig_text 1563 else: 1564 link = text 1565 insert = f'[{link}]({orig_text})' 1566 elif entity.type == MessageEntity.BOLD: 1567 insert = '*' + text + '*' 1568 elif entity.type == MessageEntity.ITALIC: 1569 insert = '_' + text + '_' 1570 elif entity.type == MessageEntity.CODE: 1571 # Monospace needs special escaping. Also can't have entities nested within 1572 insert = ( 1573 '`' 1574 + escape_markdown( 1575 orig_text, version=version, entity_type=MessageEntity.CODE 1576 ) 1577 + '`' 1578 ) 1579 elif entity.type == MessageEntity.PRE: 1580 # Monospace needs special escaping. Also can't have entities nested within 1581 code = escape_markdown( 1582 orig_text, version=version, entity_type=MessageEntity.PRE 1583 ) 1584 if entity.language: 1585 prefix = '```' + entity.language + '\n' 1586 else: 1587 if code.startswith('\\'): 1588 prefix = '```' 1589 else: 1590 prefix = '```\n' 1591 insert = prefix + code + '```' 1592 elif entity.type == MessageEntity.UNDERLINE: 1593 if version == 1: 1594 raise ValueError( 1595 'Underline entities are not supported for Markdown ' 'version 1' 1596 ) 1597 insert = '__' + text + '__' 1598 elif entity.type == MessageEntity.STRIKETHROUGH: 1599 if version == 1: 1600 raise ValueError( 1601 'Strikethrough entities are not supported for Markdown ' 'version 1' 1602 ) 1603 insert = '~' + text + '~' 1604 else: 1605 insert = text 1606 1607 if offset == 0: 1608 if sys.maxunicode == 0xFFFF: 1609 markdown_text += ( 1610 escape_markdown( 1611 message_text[last_offset : entity.offset - offset], version=version 1612 ) 1613 + insert 1614 ) 1615 else: 1616 markdown_text += ( 1617 escape_markdown( 1618 message_text[ # type: ignore 1619 last_offset * 2 : (entity.offset - offset) * 2 1620 ].decode('utf-16-le'), 1621 version=version, 1622 ) 1623 + insert 1624 ) 1625 else: 1626 if sys.maxunicode == 0xFFFF: 1627 markdown_text += ( 1628 message_text[last_offset : entity.offset - offset] + insert 1629 ) 1630 else: 1631 markdown_text += ( 1632 message_text[ # type: ignore 1633 last_offset * 2 : (entity.offset - offset) * 2 1634 ].decode('utf-16-le') 1635 + insert 1636 ) 1637 1638 last_offset = entity.offset - offset + entity.length 1639 1640 if offset == 0: 1641 if sys.maxunicode == 0xFFFF: 1642 markdown_text += escape_markdown(message_text[last_offset:], version=version) 1643 else: 1644 markdown_text += escape_markdown( 1645 message_text[last_offset * 2 :].decode('utf-16-le'), # type: ignore 1646 version=version, 1647 ) 1648 else: 1649 if sys.maxunicode == 0xFFFF: 1650 markdown_text += message_text[last_offset:] 1651 else: 1652 markdown_text += message_text[last_offset * 2 :].decode( # type: ignore 1653 'utf-16-le' 1654 ) 1655 1656 return markdown_text 1657 1658 @property 1659 def text_markdown(self) -> str: 1660 """Creates an Markdown-formatted string from the markup entities found in the message 1661 using :class:`telegram.ParseMode.MARKDOWN`. 1662 1663 Use this if you want to retrieve the message text with the entities formatted as Markdown 1664 in the same way the original message was formatted. 1665 1666 Note: 1667 :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for 1668 backward compatibility. You should use :meth:`text_markdown_v2` instead. 1669 1670 Returns: 1671 :obj:`str`: Message text with entities formatted as Markdown. 1672 1673 """ 1674 return self._parse_markdown(self.text, self.parse_entities(), urled=False) 1675 1676 @property 1677 def text_markdown_v2(self) -> str: 1678 """Creates an Markdown-formatted string from the markup entities found in the message 1679 using :class:`telegram.ParseMode.MARKDOWN_V2`. 1680 1681 Use this if you want to retrieve the message text with the entities formatted as Markdown 1682 in the same way the original message was formatted. 1683 1684 Returns: 1685 :obj:`str`: Message text with entities formatted as Markdown. 1686 1687 """ 1688 return self._parse_markdown(self.text, self.parse_entities(), urled=False, version=2) 1689 1690 @property 1691 def text_markdown_urled(self) -> str: 1692 """Creates an Markdown-formatted string from the markup entities found in the message 1693 using :class:`telegram.ParseMode.MARKDOWN`. 1694 1695 Use this if you want to retrieve the message text with the entities formatted as Markdown. 1696 This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1697 1698 Note: 1699 :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for 1700 backward compatibility. You should use :meth:`text_markdown_v2_urled` instead. 1701 1702 Returns: 1703 :obj:`str`: Message text with entities formatted as Markdown. 1704 1705 """ 1706 return self._parse_markdown(self.text, self.parse_entities(), urled=True) 1707 1708 @property 1709 def text_markdown_v2_urled(self) -> str: 1710 """Creates an Markdown-formatted string from the markup entities found in the message 1711 using :class:`telegram.ParseMode.MARKDOWN_V2`. 1712 1713 Use this if you want to retrieve the message text with the entities formatted as Markdown. 1714 This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1715 1716 Returns: 1717 :obj:`str`: Message text with entities formatted as Markdown. 1718 1719 """ 1720 return self._parse_markdown(self.text, self.parse_entities(), urled=True, version=2) 1721 1722 @property 1723 def caption_markdown(self) -> str: 1724 """Creates an Markdown-formatted string from the markup entities found in the message's 1725 caption using :class:`telegram.ParseMode.MARKDOWN`. 1726 1727 Use this if you want to retrieve the message caption with the caption entities formatted as 1728 Markdown in the same way the original message was formatted. 1729 1730 Note: 1731 :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for 1732 backward compatibility. You should use :meth:`caption_markdown_v2` instead. 1733 1734 Returns: 1735 :obj:`str`: Message caption with caption entities formatted as Markdown. 1736 1737 """ 1738 return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=False) 1739 1740 @property 1741 def caption_markdown_v2(self) -> str: 1742 """Creates an Markdown-formatted string from the markup entities found in the message's 1743 caption using :class:`telegram.ParseMode.MARKDOWN_V2`. 1744 1745 Use this if you want to retrieve the message caption with the caption entities formatted as 1746 Markdown in the same way the original message was formatted. 1747 1748 Returns: 1749 :obj:`str`: Message caption with caption entities formatted as Markdown. 1750 1751 """ 1752 return self._parse_markdown( 1753 self.caption, self.parse_caption_entities(), urled=False, version=2 1754 ) 1755 1756 @property 1757 def caption_markdown_urled(self) -> str: 1758 """Creates an Markdown-formatted string from the markup entities found in the message's 1759 caption using :class:`telegram.ParseMode.MARKDOWN`. 1760 1761 Use this if you want to retrieve the message caption with the caption entities formatted as 1762 Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1763 1764 Note: 1765 :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for 1766 backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead. 1767 1768 Returns: 1769 :obj:`str`: Message caption with caption entities formatted as Markdown. 1770 1771 """ 1772 return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=True) 1773 1774 @property 1775 def caption_markdown_v2_urled(self) -> str: 1776 """Creates an Markdown-formatted string from the markup entities found in the message's 1777 caption using :class:`telegram.ParseMode.MARKDOWN_V2`. 1778 1779 Use this if you want to retrieve the message caption with the caption entities formatted as 1780 Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. 1781 1782 Returns: 1783 :obj:`str`: Message caption with caption entities formatted as Markdown. 1784 1785 """ 1786 return self._parse_markdown( 1787 self.caption, self.parse_caption_entities(), urled=True, version=2 1788 ) 1789