diff --git a/docs/source/api/types.rst b/docs/source/api/types.rst index a18e01a6..66d409c4 100644 --- a/docs/source/api/types.rst +++ b/docs/source/api/types.rst @@ -40,7 +40,6 @@ Messages & Media :columns: 5 - :class:`Message` - - :class:`Messages` - :class:`MessageEntity` - :class:`Photo` - :class:`Thumbnail` @@ -125,7 +124,6 @@ Details .. Messages & Media .. autoclass:: Message() -.. autoclass:: Messages() .. autoclass:: MessageEntity() .. autoclass:: Photo() .. autoclass:: Thumbnail() diff --git a/pyrogram/client/ext/dispatcher.py b/pyrogram/client/ext/dispatcher.py index 2f1ec2b9..12d5a5de 100644 --- a/pyrogram/client/ext/dispatcher.py +++ b/pyrogram/client/ext/dispatcher.py @@ -21,6 +21,7 @@ import threading from collections import OrderedDict from queue import Queue from threading import Thread +from . import utils import pyrogram from pyrogram.api import types @@ -68,7 +69,7 @@ class Dispatcher: lambda upd, usr, cht: (pyrogram.Message._parse(self.client, upd.message, usr, cht), MessageHandler), Dispatcher.DELETE_MESSAGES_UPDATES: - lambda upd, usr, cht: (pyrogram.Messages._parse_deleted(self.client, upd), DeletedMessagesHandler), + lambda upd, usr, cht: (utils.parse_deleted_messages(self.client, upd), DeletedMessagesHandler), Dispatcher.CALLBACK_QUERY_UPDATES: lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler), diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py index e1959309..41270d39 100644 --- a/pyrogram/client/ext/utils.py +++ b/pyrogram/client/ext/utils.py @@ -18,8 +18,9 @@ import struct from base64 import b64decode, b64encode -from typing import Union +from typing import Union, List +import pyrogram from . import BaseClient from ...api import types @@ -135,3 +136,58 @@ def get_input_media_from_file_id( ) raise ValueError("Unknown media type: {}".format(file_id_str)) + + +def parse_messages(client, messages: types.messages.Messages, replies: int = 1) -> List["pyrogram.Message"]: + users = {i.id: i for i in messages.users} + chats = {i.id: i for i in messages.chats} + + if not messages.messages: + return pyrogram.List() + + parsed_messages = [ + pyrogram.Message._parse(client, message, users, chats, replies=0) + for message in messages.messages + ] + + if replies: + messages_with_replies = {i.id: getattr(i, "reply_to_msg_id", None) for i in messages.messages} + reply_message_ids = [i[0] for i in filter(lambda x: x[1] is not None, messages_with_replies.items())] + + if reply_message_ids: + reply_messages = client.get_messages( + parsed_messages[0].chat.id, + reply_to_message_ids=reply_message_ids, + replies=replies - 1 + ) + + for message in parsed_messages: + reply_id = messages_with_replies[message.message_id] + + for reply in reply_messages: + if reply.message_id == reply_id: + message.reply_to_message = reply + + return pyrogram.List(parsed_messages) + + +def parse_deleted_messages(client, update) -> List["pyrogram.Message"]: + messages = update.messages + channel_id = getattr(update, "channel_id", None) + + parsed_messages = [] + + for message in messages: + parsed_messages.append( + pyrogram.Message( + message_id=message, + chat=pyrogram.Chat( + id=int("-100" + str(channel_id)), + type="channel", + client=client + ) if channel_id is not None else None, + client=client + ) + ) + + return pyrogram.List(parsed_messages) diff --git a/pyrogram/client/handlers/deleted_messages_handler.py b/pyrogram/client/handlers/deleted_messages_handler.py index b6651fba..3230b9bd 100644 --- a/pyrogram/client/handlers/deleted_messages_handler.py +++ b/pyrogram/client/handlers/deleted_messages_handler.py @@ -20,16 +20,15 @@ from .handler import Handler class DeletedMessagesHandler(Handler): - """The deleted Messages handler class. Used to handle deleted messages coming from any chat - (private, group, channel). It is intended to be used with - :meth:`~Client.add_handler` + """The deleted messages handler class. Used to handle deleted messages coming from any chat + (private, group, channel). It is intended to be used with :meth:`~Client.add_handler` For a nicer way to register this handler, have a look at the :meth:`~Client.on_deleted_messages` decorator. Parameters: callback (``callable``): - Pass a function that will be called when one or more Messages have been deleted. + Pass a function that will be called when one or more messages have been deleted. It takes *(client, messages)* as positional arguments (look at the section below for a detailed description). filters (:obj:`Filters`): @@ -40,12 +39,12 @@ class DeletedMessagesHandler(Handler): client (:obj:`Client`): The Client itself, useful when you want to call other API methods inside the message handler. - messages (:obj:`Messages`): - The deleted messages. + messages (List of :obj:`Message`): + The deleted messages, as list. """ def __init__(self, callback: callable, filters=None): super().__init__(callback, filters) def check(self, messages): - return super().check(messages.messages[0]) + return super().check(messages[0]) diff --git a/pyrogram/client/methods/messages/forward_messages.py b/pyrogram/client/methods/messages/forward_messages.py index bc9ad331..c69df608 100644 --- a/pyrogram/client/methods/messages/forward_messages.py +++ b/pyrogram/client/methods/messages/forward_messages.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import Union, Iterable +from typing import Union, Iterable, List import pyrogram from pyrogram.api import functions, types @@ -32,7 +32,7 @@ class ForwardMessages(BaseClient): disable_notification: bool = None, as_copy: bool = False, remove_caption: bool = False - ) -> "pyrogram.Messages": + ) -> List["pyrogram.Message"]: """Forward messages of any kind. Parameters: @@ -64,9 +64,9 @@ class ForwardMessages(BaseClient): Defaults to False. Returns: - :obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single forwarded message is - returned, otherwise, in case *message_ids* was an iterable, the returned value will be an object containing - a list of messages, even if such iterable contained just a single element. + :obj:`Message` | List of :obj:`Message`: In case *message_ids* was an integer, the single forwarded message + is returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of + messages, even if such iterable contained just a single element. Raises: RPCError: In case of a Telegram RPC error. @@ -79,9 +79,9 @@ class ForwardMessages(BaseClient): forwarded_messages = [] for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]: - messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages + messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) - for message in messages.messages: + for message in messages: forwarded_messages.append( message.forward( chat_id, @@ -91,11 +91,7 @@ class ForwardMessages(BaseClient): ) ) - return pyrogram.Messages( - client=self, - total_count=len(forwarded_messages), - messages=forwarded_messages - ) if is_iterable else forwarded_messages[0] + return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0] else: r = self.send( functions.messages.ForwardMessages( @@ -121,8 +117,4 @@ class ForwardMessages(BaseClient): ) ) - return pyrogram.Messages( - client=self, - total_count=len(forwarded_messages), - messages=forwarded_messages - ) if is_iterable else forwarded_messages[0] + return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0] diff --git a/pyrogram/client/methods/messages/get_history.py b/pyrogram/client/methods/messages/get_history.py index c0810474..8adafe22 100644 --- a/pyrogram/client/methods/messages/get_history.py +++ b/pyrogram/client/methods/messages/get_history.py @@ -18,10 +18,11 @@ import logging import time -from typing import Union +from typing import Union, List import pyrogram from pyrogram.api import functions +from pyrogram.client.ext import utils from pyrogram.errors import FloodWait from ...ext import BaseClient @@ -37,7 +38,7 @@ class GetHistory(BaseClient): offset_id: int = 0, offset_date: int = 0, reverse: bool = False - ) -> "pyrogram.Messages": + ) -> List["pyrogram.Message"]: """Retrieve a chunk of the history of a chat. You can get up to 100 messages at once. @@ -67,15 +68,17 @@ class GetHistory(BaseClient): Pass True to retrieve the messages in reversed order (from older to most recent). Returns: - :obj:`Messages` - On success, an object containing a list of the retrieved messages. + List of :obj:`Message` - On success, a list of the retrieved messages is returned. Raises: RPCError: In case of a Telegram RPC error. """ + offset_id = offset_id or (1 if reverse else 0) + while True: try: - messages = pyrogram.Messages._parse( + messages = utils.parse_messages( self, self.send( functions.messages.GetHistory( @@ -97,6 +100,6 @@ class GetHistory(BaseClient): break if reverse: - messages.messages.reverse() + messages.reverse() return messages diff --git a/pyrogram/client/methods/messages/get_messages.py b/pyrogram/client/methods/messages/get_messages.py index 7a60f276..0f901174 100644 --- a/pyrogram/client/methods/messages/get_messages.py +++ b/pyrogram/client/methods/messages/get_messages.py @@ -18,12 +18,12 @@ import logging import time -from typing import Union, Iterable +from typing import Union, Iterable, List import pyrogram from pyrogram.api import functions, types from pyrogram.errors import FloodWait -from ...ext import BaseClient +from ...ext import BaseClient, utils log = logging.getLogger(__name__) @@ -35,7 +35,7 @@ class GetMessages(BaseClient): message_ids: Union[int, Iterable[int]] = None, reply_to_message_ids: Union[int, Iterable[int]] = None, replies: int = 1 - ) -> Union["pyrogram.Message", "pyrogram.Messages"]: + ) -> Union["pyrogram.Message", List["pyrogram.Message"]]: """Get one or more messages that belong to a specific chat. You can retrieve up to 200 messages at once. @@ -60,9 +60,9 @@ class GetMessages(BaseClient): Defaults to 1. Returns: - :obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single requested message is - returned, otherwise, in case *message_ids* was an iterable, the returned value will be an object containing - a list of messages, even if such iterable contained just a single element. + :obj:`Message` | List of :obj:`Message`: In case *message_ids* was an integer, the single requested message is + returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of messages, + even if such iterable contained just a single element. Raises: RPCError: In case of a Telegram RPC error. @@ -99,6 +99,6 @@ class GetMessages(BaseClient): else: break - messages = pyrogram.Messages._parse(self, r, replies=replies) + messages = utils.parse_messages(self, r, replies=replies) - return messages if is_iterable else messages.messages[0] + return messages if is_iterable else messages[0] diff --git a/pyrogram/client/methods/messages/iter_history.py b/pyrogram/client/methods/messages/iter_history.py index 57da3da5..15c48c95 100644 --- a/pyrogram/client/methods/messages/iter_history.py +++ b/pyrogram/client/methods/messages/iter_history.py @@ -80,7 +80,7 @@ class IterHistory(BaseClient): offset_id=offset_id, offset_date=offset_date, reverse=reverse - ).messages + ) if not messages: return diff --git a/pyrogram/client/methods/messages/send_media_group.py b/pyrogram/client/methods/messages/send_media_group.py index fb029a66..194a2202 100644 --- a/pyrogram/client/methods/messages/send_media_group.py +++ b/pyrogram/client/methods/messages/send_media_group.py @@ -38,7 +38,7 @@ class SendMediaGroup(BaseClient): media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], disable_notification: bool = None, reply_to_message_id: int = None - ): + ) -> List["pyrogram.Message"]: """Send a group of photos or videos as an album. Parameters: @@ -58,7 +58,7 @@ class SendMediaGroup(BaseClient): If the message is a reply, ID of the original message. Returns: - :obj:`Messages`: On success, an object is returned containing all the single messages sent. + List of :obj:`Message`: On success, a list of the sent messages is returned. Raises: RPCError: In case of a Telegram RPC error. @@ -158,7 +158,7 @@ class SendMediaGroup(BaseClient): else: break - return pyrogram.Messages._parse( + return utils.parse_messages( self, types.messages.Messages( messages=[m.message for m in filter( diff --git a/pyrogram/client/methods/users/get_profile_photos.py b/pyrogram/client/methods/users/get_profile_photos.py index 32e7e513..eaf632e2 100644 --- a/pyrogram/client/methods/users/get_profile_photos.py +++ b/pyrogram/client/methods/users/get_profile_photos.py @@ -20,6 +20,7 @@ from typing import Union, List import pyrogram from pyrogram.api import functions, types +from pyrogram.client.ext import utils from ...ext import BaseClient @@ -66,7 +67,7 @@ class GetProfilePhotos(BaseClient): return pyrogram.List(pyrogram.Photo._parse(self, photo) for photo in r.photos) else: - new_chat_photos = pyrogram.Messages._parse( + r = utils.parse_messages( self, self.send( functions.messages.Search( @@ -85,4 +86,4 @@ class GetProfilePhotos(BaseClient): ) ) - return pyrogram.List([m.new_chat_photo for m in new_chat_photos.messages][:limit]) + return pyrogram.List([message.new_chat_photo for message in r][:limit]) diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py index 2de2c6a3..b9bcb460 100644 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ b/pyrogram/client/types/messages_and_media/__init__.py @@ -24,7 +24,6 @@ from .game import Game from .location import Location from .message import Message from .message_entity import MessageEntity -from .messages import Messages from .photo import Photo from .poll import Poll from .poll_option import PollOption @@ -37,6 +36,6 @@ from .video_note import VideoNote from .voice import Voice __all__ = [ - "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Messages", "Photo", - "Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice" + "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail", + "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice" ] diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py index f7dff7b5..f7e99d0a 100644 --- a/pyrogram/client/types/messages_and_media/message.py +++ b/pyrogram/client/types/messages_and_media/message.py @@ -2617,7 +2617,7 @@ class Message(Object, Update): ) if self.photo: - file_id = self.photo.sizes[-1].file_id + file_id = self.photo.file_id elif self.audio: file_id = self.audio.file_id elif self.document: diff --git a/pyrogram/client/types/messages_and_media/messages.py b/pyrogram/client/types/messages_and_media/messages.py deleted file mode 100644 index ee516f20..00000000 --- a/pyrogram/client/types/messages_and_media/messages.py +++ /dev/null @@ -1,170 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès -# -# 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 List, Union - -import pyrogram -from pyrogram.api import types -from .message import Message -from ..object import Object -from ..update import Update -from ..user_and_chats import Chat - - -class Messages(Object, Update): - """Contains a chat's messages. - - Parameters: - total_count (``int``): - Total number of messages the target chat has. - - messages (List of :obj:`Message`): - Requested messages. - """ - - __slots__ = ["total_count", "messages"] - - def __init__( - self, - *, - client: "pyrogram.BaseClient" = None, - total_count: int, - messages: List[Message] - ): - super().__init__(client) - - self.total_count = total_count - self.messages = messages - - @staticmethod - def _parse(client, messages: types.messages.Messages, replies: int = 1) -> "Messages": - users = {i.id: i for i in messages.users} - chats = {i.id: i for i in messages.chats} - - total_count = getattr(messages, "count", len(messages.messages)) - - if not messages.messages: - return Messages( - total_count=total_count, - messages=[], - client=client - ) - - parsed_messages = [Message._parse(client, message, users, chats, replies=0) for message in messages.messages] - - if replies: - messages_with_replies = {i.id: getattr(i, "reply_to_msg_id", None) for i in messages.messages} - reply_message_ids = [i[0] for i in filter(lambda x: x[1] is not None, messages_with_replies.items())] - - if reply_message_ids: - reply_messages = client.get_messages( - parsed_messages[0].chat.id, - reply_to_message_ids=reply_message_ids, - replies=replies - 1 - ).messages - - for message in parsed_messages: - reply_id = messages_with_replies[message.message_id] - - for reply in reply_messages: - if reply.message_id == reply_id: - message.reply_to_message = reply - - return Messages( - total_count=total_count, - messages=parsed_messages, - client=client - ) - - @staticmethod - def _parse_deleted(client, update) -> "Messages": - messages = update.messages - channel_id = getattr(update, "channel_id", None) - - parsed_messages = [] - - for message in messages: - parsed_messages.append( - Message( - message_id=message, - chat=Chat( - id=int("-100" + str(channel_id)), - type="channel", - client=client - ) if channel_id is not None else None, - client=client - ) - ) - - return Messages( - total_count=len(parsed_messages), - messages=parsed_messages, - client=client - ) - - def forward( - self, - chat_id: Union[int, str], - disable_notification: bool = None, - as_copy: bool = False, - remove_caption: bool = False - ): - """Bound method *forward* of :obj:`Message`. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - disable_notification (``bool``, *optional*): - Sends messages silently. - Users will receive a notification with no sound. - - as_copy (``bool``, *optional*): - Pass True to forward messages without the forward header (i.e.: send a copy of the message content). - Defaults to False. - - remove_caption (``bool``, *optional*): - If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the - message. Has no effect if *as_copy* is not enabled. - Defaults to False. - - Returns: - On success, a :obj:`Messages` containing forwarded messages is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - forwarded_messages = [] - - for message in self.messages: - forwarded_messages.append( - message.forward( - chat_id=chat_id, - as_copy=as_copy, - disable_notification=disable_notification, - remove_caption=remove_caption - ) - ) - - return Messages( - total_count=len(forwarded_messages), - messages=forwarded_messages, - client=self._client - )