diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 23157631..4130dc91 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -524,6 +524,7 @@ def pyrogram_api(): Audio AvailableEffect Document + ExternalReplyInfo AlternativeVideo Animation Video diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index 6bfb0018..7cb8837f 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -27,6 +27,7 @@ from .contact import Contact from .contact_registered import ContactRegistered from .dice import Dice from .document import Document +from .external_reply_info import ExternalReplyInfo from .game import Game from .giveaway import Giveaway from .giveaway_launched import GiveawayLaunched @@ -89,6 +90,7 @@ __all__ = [ "Contact", "ContactRegistered", "Document", + "ExternalReplyInfo", "Game", "Giveaway", "GiveawayLaunched", diff --git a/pyrogram/types/messages_and_media/external_reply_info.py b/pyrogram/types/messages_and_media/external_reply_info.py new file mode 100644 index 00000000..6938462d --- /dev/null +++ b/pyrogram/types/messages_and_media/external_reply_info.py @@ -0,0 +1,327 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram 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 General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Dict, Optional + +import pyrogram +from pyrogram import enums, raw, types, utils + +from ..object import Object + + +class ExternalReplyInfo(Object): + """This object contains information about a message that is being replied to, which may come from another chat or forum topic. + + Parameters: + origin (:obj:`~pyrogram.types.MessageOrigin`, *optional*): + Origin of the message replied to by the given message. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + Chat the original message belongs to. + Available only if the chat is a supergroup or a channel. + + message_id (``int``, *optional*): + Unique message identifier inside the original chat. + Available only if the original chat is a supergroup or a channel. + + media (:obj:`~pyrogram.enums.MessageMediaType`, *optional*): + The message is a media message. + This field will contain the enumeration type of the media message. + You can use ``media = getattr(message, message.media.value)`` to access the media message. + + animation (:obj:`~pyrogram.types.Animation`, *optional*): + Message is an animation, information about the animation. + + audio (:obj:`~pyrogram.types.Audio`, *optional*): + Message is an audio file, information about the file. + + document (:obj:`~pyrogram.types.Document`, *optional*): + Message is a general file, information about the file. + + paid_media (:obj:`~pyrogram.types.PaidMediaInfo`, *optional*): + Message contains paid media; information about the paid media. + + photo (:obj:`~pyrogram.types.Photo`, *optional*): + Message is a photo, information about the photo. + + sticker (:obj:`~pyrogram.types.Sticker`, *optional*): + Message is a sticker, information about the sticker. + + story (:obj:`~pyrogram.types.Story`, *optional*): + Message is a forwarded story. + + video (:obj:`~pyrogram.types.Video`, *optional*): + Message is a video, information about the video. + + video_note (:obj:`~pyrogram.types.VideoNote`, *optional*): + Message is a video note, information about the video message. + + voice (:obj:`~pyrogram.types.Voice`, *optional*): + Message is a voice message, information about the file. + + has_media_spoiler (``bool``, *optional*): + True, if the message media is covered by a spoiler animation. + + contact (:obj:`~pyrogram.types.Contact`, *optional*): + Message is a shared contact, information about the contact. + + dice (:obj:`~pyrogram.types.Dice`, *optional*): + A dice containing a value that is randomly generated by Telegram. + + game (:obj:`~pyrogram.types.Game`, *optional*): + Message is a game, information about the game. + + giveaway (:obj:`~pyrogram.types.Giveaway`, *optional*): + Message is a scheduled giveaway, information about the giveaway. + + giveaway_result (:obj:`~pyrogram.types.GiveawayResult`, *optional*): + Message is a giveaway result, information about the giveaway result. + + invoice (:obj:`~pyrogram.types.Invoice`, *optional*): + Message is a invoice, information about the invoice. + `More about payments ยป `_ + + location (:obj:`~pyrogram.types.Location`, *optional*): + Message is a shared location, information about the location. + + poll (:obj:`~pyrogram.types.Poll`, *optional*): + Message is a native poll, information about the poll. + + venue (:obj:`~pyrogram.types.Venue`, *optional*): + Message is a venue, information about the venue. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + origin: "types.MessageOrigin" = None, + chat: "types.Chat" = None, + message_id: int, + media: Optional["enums.MessageMediaType"] = None, + animation: Optional["types.Animation"] = None, + audio: Optional["types.Audio"] = None, + document: Optional["types.Document"] = None, + paid_media: Optional["types.PaidMediaInfo"] = None, + photo: Optional["types.Photo"] = None, + sticker: Optional["types.Sticker"] = None, + story: Optional["types.Story"] = None, + video: Optional["types.Video"] = None, + video_note: Optional["types.VideoNote"] = None, + voice: Optional["types.Voice"] = None, + has_media_spoiler: Optional[bool] = None, + contact: Optional["types.Contact"] = None, + dice: Optional["types.Dice"] = None, + game: Optional["types.Game"] = None, + giveaway: Optional["types.Giveaway"] = None, + giveaway_result: Optional["types.GiveawayResult"] = None, + invoice: Optional["types.Invoice"] = None, + location: Optional["types.Location"] = None, + poll: Optional["types.Poll"] = None, + venue: Optional["types.Venue"] = None, + ): + super().__init__(client) + + self.origin = origin + self.chat = chat + self.message_id = message_id + self.media = media + self.animation = animation + self.audio = audio + self.document = document + self.paid_media = paid_media + self.photo = photo + self.sticker = sticker + self.story = story + self.video = video + self.video_note = video_note + self.voice = voice + self.has_media_spoiler = has_media_spoiler + self.contact = contact + self.dice = dice + self.game = game + self.giveaway = giveaway + self.giveaway_result = giveaway_result + self.invoice = invoice + self.location = location + self.poll = poll + self.venue = venue + + @staticmethod + async def _parse( + client, + reply: "raw.types.MessageReplyHeader", + users: Dict[int, "raw.types.User"], + chats: Dict[int, "raw.types.Chat"], + ) -> Optional["ExternalReplyInfo"]: + if not isinstance(reply, raw.types.MessageReplyHeader): + return None + + if not reply.reply_from: + return None + + animation = None + audio = None + document = None + paid_media = None + photo = None + sticker = None + story = None + video = None + video_note = None + voice = None + contact = None + dice = None + game = None + giveaway = None + giveaway_result = None + invoice = None + location = None + poll = None + venue = None + + media = reply.reply_media + media_type = None + has_media_spoiler = None + + if media: + if isinstance(media, raw.types.MessageMediaPhoto): + photo = types.Photo._parse(client, media.photo, media.ttl_seconds) + media_type = enums.MessageMediaType.PHOTO + has_media_spoiler = media.spoiler + elif isinstance(media, raw.types.MessageMediaGeo): + location = types.Location._parse(client, media.geo) + media_type = enums.MessageMediaType.LOCATION + elif isinstance(media, raw.types.MessageMediaContact): + contact = types.Contact._parse(client, media) + media_type = enums.MessageMediaType.CONTACT + elif isinstance(media, raw.types.MessageMediaVenue): + venue = types.Venue._parse(client, media) + media_type = enums.MessageMediaType.VENUE + elif isinstance(media, raw.types.MessageMediaGame): + game = types.Game._parse(client, media) + media_type = enums.MessageMediaType.GAME + elif isinstance(media, raw.types.MessageMediaGiveaway): + giveaway = types.Giveaway._parse(client, reply) + media_type = enums.MessageMediaType.GIVEAWAY + elif isinstance(media, raw.types.MessageMediaGiveawayResults): + giveaway_result = await types.GiveawayResult._parse(client, media) + media_type = enums.MessageMediaType.GIVEAWAY_RESULT + elif isinstance(media, raw.types.MessageMediaInvoice): + invoice = types.Invoice._parse(media) + media_type = enums.MessageMediaType.INVOICE + elif isinstance(media, raw.types.MessageMediaStory): + story = await types.Story._parse(client, media, media.peer) + media_type = enums.MessageMediaType.STORY + elif isinstance(media, raw.types.MessageMediaDocument): + doc = media.document + + if isinstance(doc, raw.types.Document): + attributes = {type(i): i for i in doc.attributes} + + file_name = getattr( + attributes.get( + raw.types.DocumentAttributeFilename, None + ), "file_name", None + ) + + if raw.types.DocumentAttributeAnimated in attributes: + video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None) + animation = types.Animation._parse(client, doc, video_attributes, file_name) + media_type = enums.MessageMediaType.ANIMATION + has_media_spoiler = media.spoiler + elif raw.types.DocumentAttributeSticker in attributes: + sticker = await types.Sticker._parse(client, doc, attributes) + media_type = enums.MessageMediaType.STICKER + elif raw.types.DocumentAttributeVideo in attributes: + video_attributes = attributes[raw.types.DocumentAttributeVideo] + + if video_attributes.round_message: + video_note = types.VideoNote._parse(client, doc, video_attributes) + media_type = enums.MessageMediaType.VIDEO_NOTE + else: + video = types.Video._parse( + client, + doc, + video_attributes, + file_name, + media.ttl_seconds, + media.video_cover, + media.video_timestamp + ) + media_type = enums.MessageMediaType.VIDEO + has_media_spoiler = media.spoiler + elif raw.types.DocumentAttributeAudio in attributes: + audio_attributes = attributes[raw.types.DocumentAttributeAudio] + + if audio_attributes.voice: + voice = types.Voice._parse(client, doc, audio_attributes) + media_type = enums.MessageMediaType.VOICE + else: + audio = types.Audio._parse(client, doc, audio_attributes, file_name) + media_type = enums.MessageMediaType.AUDIO + else: + document = types.Document._parse(client, doc, file_name) + media_type = enums.MessageMediaType.DOCUMENT + elif isinstance(media, raw.types.MessageMediaPoll): + poll = types.Poll._parse(client, media, users) + media_type = enums.MessageMediaType.POLL + elif isinstance(media, raw.types.MessageMediaDice): + dice = types.Dice._parse(client, media) + media_type = enums.MessageMediaType.DICE + elif isinstance(media, raw.types.MessageMediaPaidMedia): + paid_media = types.PaidMedia._parse(client, media) + media_type = enums.MessageMediaType.PAID_MEDIA + else: + media = None + + return ExternalReplyInfo( + origin=types.MessageOrigin._parse( + client, + reply.reply_from, + users, + chats, + ), + chat=types.Chat._parse_chat( + client, + chats.get(utils.get_raw_peer_id(reply.reply_to_peer_id)), + ), + message_id=reply.reply_to_msg_id, + media=media_type, + animation=animation, + audio=audio, + document=document, + paid_media=paid_media, + photo=photo, + sticker=sticker, + story=story, + video=video, + video_note=video_note, + voice=voice, + has_media_spoiler=has_media_spoiler, + contact=contact, + dice=dice, + game=game, + giveaway=giveaway, + giveaway_result=giveaway_result, + invoice=invoice, + location=location, + poll=poll, + venue=venue + ) diff --git a/pyrogram/types/messages_and_media/game.py b/pyrogram/types/messages_and_media/game.py index 930f45b0..e7c97f08 100644 --- a/pyrogram/types/messages_and_media/game.py +++ b/pyrogram/types/messages_and_media/game.py @@ -69,12 +69,11 @@ class Game(Object): self.animation = animation @staticmethod - def _parse(client, message: "raw.types.Message") -> "Game": - game: "raw.types.Game" = message.media.game + def _parse(client, media: "raw.types.MessageMediaGame") -> "Game": animation = None - if game.document: - attributes = {type(i): i for i in game.document.attributes} + if media.game.document: + attributes = {type(i): i for i in media.game.document.attributes} file_name = getattr( attributes.get( @@ -84,17 +83,17 @@ class Game(Object): animation = types.Animation._parse( client, - game.document, + media.game.document, attributes.get(raw.types.DocumentAttributeVideo, None), file_name ) return Game( - id=game.id, - title=game.title, - short_name=game.short_name, - description=game.description, - photo=types.Photo._parse(client, game.photo), + id=media.game.id, + title=media.game.title, + short_name=media.game.short_name, + description=media.game.description, + photo=types.Photo._parse(client, media.game.photo), animation=animation, client=client ) diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index e62398dd..2449cf10 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -318,6 +318,9 @@ class Message(Object, Update): Messages sent from yourself to other chats are outgoing (*outgoing* is True). An exception is made for your own personal chat; messages sent there will be incoming. + external_reply (:obj:`~pyrogram.types.ExternalReplyInfo`, *optional*): + Information about the message that is being replied to, which may come from another chat or forum topic. + matches (List of regex Matches, *optional*): A list containing all `Match Objects `_ that match the text of this message. Only applicable when using :obj:`Filters.regex `. @@ -507,6 +510,7 @@ class Message(Object, Update): forwards: int = None, via_bot: "types.User" = None, outgoing: bool = None, + external_reply: Optional["types.ExternalReplyInfo"] = None, matches: List[Match] = None, command: List[str] = None, bot_allowed: "types.BotAllowed" = None, @@ -546,8 +550,8 @@ class Message(Object, Update): self.sender_business_bot = sender_business_bot self.date = date self.chat = chat - self.topic = topic self.forward_origin = forward_origin + self.external_reply = external_reply self.is_topic_message = is_topic_message self.reply_to_chat_id = reply_to_chat_id self.reply_to_message_id = reply_to_message_id @@ -1032,7 +1036,7 @@ class Message(Object, Update): venue = types.Venue._parse(client, media) media_type = enums.MessageMediaType.VENUE elif isinstance(media, raw.types.MessageMediaGame): - game = types.Game._parse(client, message) + game = types.Game._parse(client, media) media_type = enums.MessageMediaType.GAME elif isinstance(media, raw.types.MessageMediaGiveaway): giveaway = await types.Giveaway._parse(client, message) @@ -1221,6 +1225,12 @@ class Message(Object, Update): parsed_message.sender_chat = sender_chat if message.reply_to: + parsed_message.external_reply = await types.ExternalReplyInfo._parse( + client, + message.reply_to, + users, + chats + ) if isinstance(message.reply_to, raw.types.MessageReplyHeader): if message.reply_to.quote: parsed_message.quote = types.TextQuote._parse(