Merge pull request #251 from pyrogram/types-revamp

Types revamp
This commit is contained in:
Dan 2019-06-12 10:48:10 +02:00 committed by GitHub
commit a21858a262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 206 additions and 579 deletions

View File

@ -8,4 +8,5 @@ REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again la
RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try again later WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try again later
INTERDC_X_CALL_ERROR Telegram is having internal problems. Please try again later INTERDC_X_CALL_ERROR Telegram is having internal problems. Please try again later
INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems. Please try again later INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems. Please try again later
FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later
1 id message
8 RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
9 WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try again later
10 INTERDC_X_CALL_ERROR Telegram is having internal problems. Please try again later
11 INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems. Please try again later
12 FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later

View File

@ -30,10 +30,8 @@ Users & Chats
- :class:`ChatPreview` - :class:`ChatPreview`
- :class:`ChatPhoto` - :class:`ChatPhoto`
- :class:`ChatMember` - :class:`ChatMember`
- :class:`ChatMembers`
- :class:`ChatPermissions` - :class:`ChatPermissions`
- :class:`Dialog` - :class:`Dialog`
- :class:`Dialogs`
Messages & Media Messages & Media
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
@ -42,10 +40,8 @@ Messages & Media
:columns: 5 :columns: 5
- :class:`Message` - :class:`Message`
- :class:`Messages`
- :class:`MessageEntity` - :class:`MessageEntity`
- :class:`Photo` - :class:`Photo`
- :class:`ProfilePhotos`
- :class:`Thumbnail` - :class:`Thumbnail`
- :class:`Audio` - :class:`Audio`
- :class:`Document` - :class:`Document`
@ -61,8 +57,8 @@ Messages & Media
- :class:`Poll` - :class:`Poll`
- :class:`PollOption` - :class:`PollOption`
Keyboards Bots & Keyboards
^^^^^^^^^ ^^^^^^^^^^^^^^^^
.. hlist:: .. hlist::
:columns: 4 :columns: 4
@ -75,7 +71,6 @@ Keyboards
- :class:`ForceReply` - :class:`ForceReply`
- :class:`CallbackQuery` - :class:`CallbackQuery`
- :class:`GameHighScore` - :class:`GameHighScore`
- :class:`GameHighScores`
- :class:`CallbackGame` - :class:`CallbackGame`
Input Media Input Media
@ -123,17 +118,13 @@ Details
.. autoclass:: ChatPreview() .. autoclass:: ChatPreview()
.. autoclass:: ChatPhoto() .. autoclass:: ChatPhoto()
.. autoclass:: ChatMember() .. autoclass:: ChatMember()
.. autoclass:: ChatMembers()
.. autoclass:: ChatPermissions() .. autoclass:: ChatPermissions()
.. autoclass:: Dialog() .. autoclass:: Dialog()
.. autoclass:: Dialogs()
.. Messages & Media .. Messages & Media
.. autoclass:: Message() .. autoclass:: Message()
.. autoclass:: Messages()
.. autoclass:: MessageEntity() .. autoclass:: MessageEntity()
.. autoclass:: Photo() .. autoclass:: Photo()
.. autoclass:: ProfilePhotos()
.. autoclass:: Thumbnail() .. autoclass:: Thumbnail()
.. autoclass:: Audio() .. autoclass:: Audio()
.. autoclass:: Document() .. autoclass:: Document()
@ -149,7 +140,7 @@ Details
.. autoclass:: Poll() .. autoclass:: Poll()
.. autoclass:: PollOption() .. autoclass:: PollOption()
.. Keyboards .. Bots & Keyboards
.. autoclass:: ReplyKeyboardMarkup() .. autoclass:: ReplyKeyboardMarkup()
.. autoclass:: KeyboardButton() .. autoclass:: KeyboardButton()
.. autoclass:: ReplyKeyboardRemove() .. autoclass:: ReplyKeyboardRemove()
@ -158,7 +149,6 @@ Details
.. autoclass:: ForceReply() .. autoclass:: ForceReply()
.. autoclass:: CallbackQuery() .. autoclass:: CallbackQuery()
.. autoclass:: GameHighScore() .. autoclass:: GameHighScore()
.. autoclass:: GameHighScores()
.. autoclass:: CallbackGame() .. autoclass:: CallbackGame()
.. Input Media .. Input Media

View File

@ -19,6 +19,5 @@
from .base_client import BaseClient from .base_client import BaseClient
from .dispatcher import Dispatcher from .dispatcher import Dispatcher
from .emoji import Emoji from .emoji import Emoji
from .syncer import Syncer
from .file_data import FileData from .file_data import FileData
from .syncer import Syncer

View File

@ -24,6 +24,7 @@ from threading import Thread
import pyrogram import pyrogram
from pyrogram.api import types from pyrogram.api import types
from . import utils
from ..handlers import ( from ..handlers import (
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler
@ -68,7 +69,7 @@ class Dispatcher:
lambda upd, usr, cht: (pyrogram.Message._parse(self.client, upd.message, usr, cht), MessageHandler), lambda upd, usr, cht: (pyrogram.Message._parse(self.client, upd.message, usr, cht), MessageHandler),
Dispatcher.DELETE_MESSAGES_UPDATES: 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: Dispatcher.CALLBACK_QUERY_UPDATES:
lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler), lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler),

View File

@ -18,8 +18,9 @@
import struct import struct
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from typing import Union from typing import Union, List
import pyrogram
from . import BaseClient from . import BaseClient
from ...api import types from ...api import types
@ -135,3 +136,58 @@ def get_input_media_from_file_id(
) )
raise ValueError("Unknown media type: {}".format(file_id_str)) 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)

View File

@ -19,7 +19,7 @@
import re import re
from .filter import Filter from .filter import Filter
from ..types.keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup from ..types.bots_and_keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup
def create(name: str, func: callable, **kwargs) -> type: def create(name: str, func: callable, **kwargs) -> type:

View File

@ -20,16 +20,15 @@ from .handler import Handler
class DeletedMessagesHandler(Handler): class DeletedMessagesHandler(Handler):
"""The deleted Messages handler class. Used to handle deleted messages coming from any chat """The deleted messages handler class. Used to handle deleted messages coming from any chat
(private, group, channel). It is intended to be used with (private, group, channel). It is intended to be used with :meth:`~Client.add_handler`
:meth:`~Client.add_handler`
For a nicer way to register this handler, have a look at the For a nicer way to register this handler, have a look at the
:meth:`~Client.on_deleted_messages` decorator. :meth:`~Client.on_deleted_messages` decorator.
Parameters: Parameters:
callback (``callable``): 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). It takes *(client, messages)* as positional arguments (look at the section below for a detailed description).
filters (:obj:`Filters`): filters (:obj:`Filters`):
@ -40,12 +39,12 @@ class DeletedMessagesHandler(Handler):
client (:obj:`Client`): client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the message handler. The Client itself, useful when you want to call other API methods inside the message handler.
messages (:obj:`Messages`): messages (List of :obj:`Message`):
The deleted messages. The deleted messages, as list.
""" """
def __init__(self, callback: callable, filters=None): def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters) super().__init__(callback, filters)
def check(self, messages): def check(self, messages):
return super().check(messages.messages[0]) return super().check(messages[0])

View File

@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union from typing import Union, List
import pyrogram import pyrogram
from pyrogram.api import functions from pyrogram.api import functions
@ -29,7 +29,7 @@ class GetGameHighScores(BaseClient):
user_id: Union[int, str], user_id: Union[int, str],
chat_id: Union[int, str], chat_id: Union[int, str],
message_id: int = None message_id: int = None
) -> "pyrogram.GameHighScores": ) -> List["pyrogram.GameHighScore"]:
"""Get data for high score tables. """Get data for high score tables.
Parameters: Parameters:
@ -49,20 +49,19 @@ class GetGameHighScores(BaseClient):
Required if inline_message_id is not specified. Required if inline_message_id is not specified.
Returns: Returns:
:obj:`GameHighScores`: On success. List of :obj:`GameHighScore`: On success.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
""" """
# TODO: inline_message_id # TODO: inline_message_id
return pyrogram.GameHighScores._parse( r = self.send(
self, functions.messages.GetGameHighScores(
self.send( peer=self.resolve_peer(chat_id),
functions.messages.GetGameHighScores( id=message_id,
peer=self.resolve_peer(chat_id), user_id=self.resolve_peer(user_id)
id=message_id,
user_id=self.resolve_peer(user_id)
)
) )
) )
return pyrogram.List(pyrogram.GameHighScore._parse(self, score, r.users) for score in r.scores)

View File

@ -51,13 +51,18 @@ class GetChatMember(BaseClient):
user = self.resolve_peer(user_id) user = self.resolve_peer(user_id)
if isinstance(chat, types.InputPeerChat): if isinstance(chat, types.InputPeerChat):
full_chat = self.send( r = self.send(
functions.messages.GetFullChat( functions.messages.GetFullChat(
chat_id=chat.chat_id chat_id=chat.chat_id
) )
) )
for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members: members = r.full_chat.participants.participants
users = {i.id: i for i in r.users}
for member in members:
member = pyrogram.ChatMember._parse(self, member, users)
if isinstance(user, types.InputPeerSelf): if isinstance(user, types.InputPeerSelf):
if member.user.is_self: if member.user.is_self:
return member return member

View File

@ -18,7 +18,7 @@
import logging import logging
import time import time
from typing import Union from typing import Union, List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
@ -45,7 +45,7 @@ class GetChatMembers(BaseClient):
limit: int = 200, limit: int = 200,
query: str = "", query: str = "",
filter: str = Filters.ALL filter: str = Filters.ALL
) -> "pyrogram.ChatMembers": ) -> List["pyrogram.ChatMember"]:
"""Get a chunk of the members list of a chat. """Get a chunk of the members list of a chat.
You can get up to 200 chat members at once. You can get up to 200 chat members at once.
@ -59,15 +59,16 @@ class GetChatMembers(BaseClient):
offset (``int``, *optional*): offset (``int``, *optional*):
Sequential number of the first member to be returned. Sequential number of the first member to be returned.
Defaults to 0 [1]_. Only applicable to supergroups and channels. Defaults to 0 [1]_.
limit (``int``, *optional*): limit (``int``, *optional*):
Limits the number of members to be retrieved. Limits the number of members to be retrieved.
Only applicable to supergroups and channels.
Defaults to 200, which is also the maximum server limit allowed per method call. Defaults to 200, which is also the maximum server limit allowed per method call.
query (``str``, *optional*): query (``str``, *optional*):
Query string to filter members based on their display names and usernames. Query string to filter members based on their display names and usernames.
Defaults to "" (empty string) [2]_. Only applicable to supergroups and channels. Defaults to "" (empty string) [2]_.
filter (``str``, *optional*): filter (``str``, *optional*):
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
@ -78,6 +79,7 @@ class GetChatMembers(BaseClient):
*"bots"* - bots only, *"bots"* - bots only,
*"recent"* - recent members only, *"recent"* - recent members only,
*"administrators"* - chat administrators only. *"administrators"* - chat administrators only.
Only applicable to supergroups and channels.
Defaults to *"all"*. Defaults to *"all"*.
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members .. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
@ -86,7 +88,7 @@ class GetChatMembers(BaseClient):
.. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only. .. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only.
Returns: Returns:
:obj:`ChatMembers`: On success, an object containing a list of chat members is returned. List of :obj:`ChatMember`: On success, a list of chat members is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -95,14 +97,16 @@ class GetChatMembers(BaseClient):
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat): if isinstance(peer, types.InputPeerChat):
return pyrogram.ChatMembers._parse( r = self.send(
self, functions.messages.GetFullChat(
self.send( chat_id=peer.chat_id
functions.messages.GetFullChat(
chat_id=peer.chat_id
)
) )
) )
members = r.full_chat.participants.participants
users = {i.id: i for i in r.users}
return pyrogram.List(pyrogram.ChatMember._parse(self, member, users) for member in members)
elif isinstance(peer, types.InputPeerChannel): elif isinstance(peer, types.InputPeerChannel):
filter = filter.lower() filter = filter.lower()
@ -123,18 +127,20 @@ class GetChatMembers(BaseClient):
while True: while True:
try: try:
return pyrogram.ChatMembers._parse( r = self.send(
self, functions.channels.GetParticipants(
self.send( channel=peer,
functions.channels.GetParticipants( filter=filter,
channel=peer, offset=offset,
filter=filter, limit=limit,
offset=offset, hash=0
limit=limit,
hash=0
)
) )
) )
members = r.participants
users = {i.id: i for i in r.users}
return pyrogram.List(pyrogram.ChatMember._parse(self, member, users) for member in members)
except FloodWait as e: except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x)) log.warning("Sleeping for {}s".format(e.x))
time.sleep(e.x) time.sleep(e.x)

View File

@ -18,6 +18,7 @@
import logging import logging
import time import time
from typing import List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
@ -33,7 +34,7 @@ class GetDialogs(BaseClient):
offset_date: int = 0, offset_date: int = 0,
limit: int = 100, limit: int = 100,
pinned_only: bool = False pinned_only: bool = False
) -> "pyrogram.Dialogs": ) -> List["pyrogram.Dialog"]:
"""Get a chunk of the user's dialogs. """Get a chunk of the user's dialogs.
You can get up to 100 dialogs at once. You can get up to 100 dialogs at once.
@ -53,7 +54,7 @@ class GetDialogs(BaseClient):
Defaults to False. Defaults to False.
Returns: Returns:
:obj:`Dialogs`: On success, an object containing a list of dialogs is returned. List of :obj:`Dialog`: On success, a list of dialogs is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -80,4 +81,32 @@ class GetDialogs(BaseClient):
else: else:
break break
return pyrogram.Dialogs._parse(self, r) users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
messages = {}
for message in r.messages:
to_id = message.to_id
if isinstance(to_id, types.PeerUser):
if message.out:
chat_id = to_id.user_id
else:
chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else:
chat_id = int("-100" + str(to_id.channel_id))
messages[chat_id] = pyrogram.Message._parse(self, message, users, chats)
parsed_dialogs = []
for dialog in r.dialogs:
if not isinstance(dialog, types.Dialog):
continue
parsed_dialogs.append(pyrogram.Dialog._parse(self, dialog, messages, users, chats))
return pyrogram.List(parsed_dialogs)

View File

@ -106,7 +106,7 @@ class IterChatMembers(BaseClient):
limit=limit, limit=limit,
query=q, query=q,
filter=filter filter=filter
).chat_members )
if not chat_members: if not chat_members:
break break

View File

@ -55,7 +55,7 @@ class IterDialogs(BaseClient):
pinned_dialogs = self.get_dialogs( pinned_dialogs = self.get_dialogs(
pinned_only=True pinned_only=True
).dialogs )
for dialog in pinned_dialogs: for dialog in pinned_dialogs:
yield dialog yield dialog
@ -69,7 +69,7 @@ class IterDialogs(BaseClient):
dialogs = self.get_dialogs( dialogs = self.get_dialogs(
offset_date=offset_date, offset_date=offset_date,
limit=limit limit=limit
).dialogs )
if not dialogs: if not dialogs:
return return

View File

@ -47,4 +47,4 @@ class GetContacts(BaseClient):
time.sleep(e.x) time.sleep(e.x)
else: else:
log.info("Total contacts: {}".format(len(self.peers_by_phone))) log.info("Total contacts: {}".format(len(self.peers_by_phone)))
return [pyrogram.User._parse(self, user) for user in contacts.users] return pyrogram.List(pyrogram.User._parse(self, user) for user in contacts.users)

View File

@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, Iterable from typing import Union, Iterable, List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
@ -32,7 +32,7 @@ class ForwardMessages(BaseClient):
disable_notification: bool = None, disable_notification: bool = None,
as_copy: bool = False, as_copy: bool = False,
remove_caption: bool = False remove_caption: bool = False
) -> "pyrogram.Messages": ) -> List["pyrogram.Message"]:
"""Forward messages of any kind. """Forward messages of any kind.
Parameters: Parameters:
@ -64,9 +64,9 @@ class ForwardMessages(BaseClient):
Defaults to False. Defaults to False.
Returns: Returns:
:obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single forwarded message is :obj:`Message` | List of :obj:`Message`: In case *message_ids* was an integer, the single forwarded message
returned, otherwise, in case *message_ids* was an iterable, the returned value will be an object containing is returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of
a list of messages, even if such iterable contained just a single element. messages, even if such iterable contained just a single element.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -79,9 +79,9 @@ class ForwardMessages(BaseClient):
forwarded_messages = [] forwarded_messages = []
for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]: 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( forwarded_messages.append(
message.forward( message.forward(
chat_id, chat_id,
@ -91,11 +91,7 @@ class ForwardMessages(BaseClient):
) )
) )
return pyrogram.Messages( return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0]
client=self,
total_count=len(forwarded_messages),
messages=forwarded_messages
) if is_iterable else forwarded_messages[0]
else: else:
r = self.send( r = self.send(
functions.messages.ForwardMessages( functions.messages.ForwardMessages(
@ -121,8 +117,4 @@ class ForwardMessages(BaseClient):
) )
) )
return pyrogram.Messages( return pyrogram.List(forwarded_messages) if is_iterable else forwarded_messages[0]
client=self,
total_count=len(forwarded_messages),
messages=forwarded_messages
) if is_iterable else forwarded_messages[0]

View File

@ -18,10 +18,11 @@
import logging import logging
import time import time
from typing import Union from typing import Union, List
import pyrogram import pyrogram
from pyrogram.api import functions from pyrogram.api import functions
from pyrogram.client.ext import utils
from pyrogram.errors import FloodWait from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient
@ -37,7 +38,7 @@ class GetHistory(BaseClient):
offset_id: int = 0, offset_id: int = 0,
offset_date: int = 0, offset_date: int = 0,
reverse: bool = False reverse: bool = False
) -> "pyrogram.Messages": ) -> List["pyrogram.Message"]:
"""Retrieve a chunk of the history of a chat. """Retrieve a chunk of the history of a chat.
You can get up to 100 messages at once. 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). Pass True to retrieve the messages in reversed order (from older to most recent).
Returns: 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: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
""" """
offset_id = offset_id or (1 if reverse else 0)
while True: while True:
try: try:
messages = pyrogram.Messages._parse( messages = utils.parse_messages(
self, self,
self.send( self.send(
functions.messages.GetHistory( functions.messages.GetHistory(
@ -97,6 +100,6 @@ class GetHistory(BaseClient):
break break
if reverse: if reverse:
messages.messages.reverse() messages.reverse()
return messages return messages

View File

@ -18,12 +18,12 @@
import logging import logging
import time import time
from typing import Union, Iterable from typing import Union, Iterable, List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.errors import FloodWait from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient, utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -35,7 +35,7 @@ class GetMessages(BaseClient):
message_ids: Union[int, Iterable[int]] = None, message_ids: Union[int, Iterable[int]] = None,
reply_to_message_ids: Union[int, Iterable[int]] = None, reply_to_message_ids: Union[int, Iterable[int]] = None,
replies: int = 1 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. """Get one or more messages that belong to a specific chat.
You can retrieve up to 200 messages at once. You can retrieve up to 200 messages at once.
@ -60,9 +60,9 @@ class GetMessages(BaseClient):
Defaults to 1. Defaults to 1.
Returns: Returns:
:obj:`Message` | :obj:`Messages`: In case *message_ids* was an integer, the single requested message is :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 an object containing returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of messages,
a list of messages, even if such iterable contained just a single element. even if such iterable contained just a single element.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -99,6 +99,6 @@ class GetMessages(BaseClient):
else: else:
break 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]

View File

@ -80,7 +80,7 @@ class IterHistory(BaseClient):
offset_id=offset_id, offset_id=offset_id,
offset_date=offset_date, offset_date=offset_date,
reverse=reverse reverse=reverse
).messages )
if not messages: if not messages:
return return

View File

@ -38,7 +38,7 @@ class SendMediaGroup(BaseClient):
media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None reply_to_message_id: int = None
): ) -> List["pyrogram.Message"]:
"""Send a group of photos or videos as an album. """Send a group of photos or videos as an album.
Parameters: Parameters:
@ -58,7 +58,7 @@ class SendMediaGroup(BaseClient):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
Returns: 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: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -158,7 +158,7 @@ class SendMediaGroup(BaseClient):
else: else:
break break
return pyrogram.Messages._parse( return utils.parse_messages(
self, self,
types.messages.Messages( types.messages.Messages(
messages=[m.message for m in filter( messages=[m.message for m in filter(

View File

@ -16,10 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union from typing import Union, List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.client.ext import utils
from ...ext import BaseClient from ...ext import BaseClient
@ -29,7 +30,7 @@ class GetProfilePhotos(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
offset: int = 0, offset: int = 0,
limit: int = 100 limit: int = 100
) -> "pyrogram.ProfilePhotos": ) -> List["pyrogram.Photo"]:
"""Get a list of profile pictures for a user or a chat. """Get a list of profile pictures for a user or a chat.
Parameters: Parameters:
@ -47,7 +48,7 @@ class GetProfilePhotos(BaseClient):
Values between 1100 are accepted. Defaults to 100. Values between 1100 are accepted. Defaults to 100.
Returns: Returns:
:obj:`ProfilePhotos`: On success, an object containing a list of the profile photos is returned. List of :obj:`Photo`: On success, a list of profile photos is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error. RPCError: In case of a Telegram RPC error.
@ -55,19 +56,18 @@ class GetProfilePhotos(BaseClient):
peer_id = self.resolve_peer(chat_id) peer_id = self.resolve_peer(chat_id)
if isinstance(peer_id, (types.InputPeerUser, types.InputPeerSelf)): if isinstance(peer_id, (types.InputPeerUser, types.InputPeerSelf)):
return pyrogram.ProfilePhotos._parse( r = self.send(
self, functions.photos.GetUserPhotos(
self.send( user_id=peer_id,
functions.photos.GetUserPhotos( offset=offset,
user_id=peer_id, max_id=0,
offset=offset, limit=limit
max_id=0,
limit=limit
)
) )
) )
return pyrogram.List(pyrogram.Photo._parse(self, photo) for photo in r.photos)
else: else:
new_chat_photos = pyrogram.Messages._parse( r = utils.parse_messages(
self, self,
self.send( self.send(
functions.messages.Search( functions.messages.Search(
@ -86,7 +86,4 @@ class GetProfilePhotos(BaseClient):
) )
) )
return pyrogram.ProfilePhotos( return pyrogram.List([message.new_chat_photo for message in r][:limit])
total_count=new_chat_photos.total_count,
profile_photos=[m.new_chat_photo for m in new_chat_photos.messages][:limit]
)

View File

@ -56,7 +56,7 @@ class GetUsers(BaseClient):
) )
) )
users = [] users = pyrogram.List()
for i in r: for i in r:
users.append(pyrogram.User._parse(self, i)) users.append(pyrogram.User._parse(self, i))

View File

@ -63,7 +63,7 @@ class IterProfilePhotos(BaseClient):
chat_id=chat_id, chat_id=chat_id,
offset=offset, offset=offset,
limit=limit limit=limit
).photos )
if not photos: if not photos:
return return

View File

@ -16,10 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .keyboards import * from .bots_and_keyboards import *
from .inline_mode import * from .inline_mode import *
from .input_media import * from .input_media import *
from .input_message_content import * from .input_message_content import *
from .list import List
from .messages_and_media import * from .messages_and_media import *
from .object import Object
from .update import * from .update import *
from .user_and_chats import * from .user_and_chats import *

View File

@ -20,7 +20,6 @@ from .callback_game import CallbackGame
from .callback_query import CallbackQuery from .callback_query import CallbackQuery
from .force_reply import ForceReply from .force_reply import ForceReply
from .game_high_score import GameHighScore from .game_high_score import GameHighScore
from .game_high_scores import GameHighScores
from .inline_keyboard_button import InlineKeyboardButton from .inline_keyboard_button import InlineKeyboardButton
from .inline_keyboard_markup import InlineKeyboardMarkup from .inline_keyboard_markup import InlineKeyboardMarkup
from .keyboard_button import KeyboardButton from .keyboard_button import KeyboardButton
@ -28,6 +27,6 @@ from .reply_keyboard_markup import ReplyKeyboardMarkup
from .reply_keyboard_remove import ReplyKeyboardRemove from .reply_keyboard_remove import ReplyKeyboardRemove
__all__ = [ __all__ = [
"CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "GameHighScores", "InlineKeyboardButton", "CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "InlineKeyboardButton", "InlineKeyboardMarkup",
"InlineKeyboardMarkup", "KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove" "KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove"
] ]

View File

@ -1,60 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from typing import List
import pyrogram
from pyrogram.api import types
from pyrogram.client.types.object import Object
from .game_high_score import GameHighScore
class GameHighScores(Object):
"""The high scores table for a game.
Parameters:
total_count (``int``):
Total number of scores the target game has.
game_high_scores (List of :obj:`GameHighScore`):
Game scores.
"""
__slots__ = ["total_count", "game_high_scores"]
def __init__(
self,
*,
client: "pyrogram.BaseClient" = None,
total_count: int,
game_high_scores: List[GameHighScore]
):
super().__init__(client)
self.total_count = total_count
self.game_high_scores = game_high_scores
@staticmethod
def _parse(client, game_high_scores: types.messages.HighScores) -> "GameHighScores":
return GameHighScores(
total_count=len(game_high_scores.scores),
game_high_scores=[
GameHighScore._parse(client, score, game_high_scores.users)
for score in game_high_scores.scores],
client=client
)

View File

@ -24,11 +24,9 @@ from .game import Game
from .location import Location from .location import Location
from .message import Message from .message import Message
from .message_entity import MessageEntity from .message_entity import MessageEntity
from .messages import Messages
from .photo import Photo from .photo import Photo
from .poll import Poll from .poll import Poll
from .poll_option import PollOption from .poll_option import PollOption
from .profile_photos import ProfilePhotos
from .sticker import Sticker from .sticker import Sticker
from .stripped_thumbnail import StrippedThumbnail from .stripped_thumbnail import StrippedThumbnail
from .thumbnail import Thumbnail from .thumbnail import Thumbnail
@ -38,7 +36,6 @@ from .video_note import VideoNote
from .voice import Voice from .voice import Voice
__all__ = [ __all__ = [
"Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Messages", "Photo", "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail",
"Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "ProfilePhotos", "Venue", "Video", "VideoNote", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice"
"Voice"
] ]

View File

@ -2617,7 +2617,7 @@ class Message(Object, Update):
) )
if self.photo: if self.photo:
file_id = self.photo.sizes[-1].file_id file_id = self.photo.file_id
elif self.audio: elif self.audio:
file_id = self.audio.file_id file_id = self.audio.file_id
elif self.document: elif self.document:

View File

@ -1,170 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
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
)

View File

@ -1,57 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from typing import List
import pyrogram
from .photo import Photo
from ..object import Object
class ProfilePhotos(Object):
"""Contains a user's profile pictures.
Parameters:
total_count (``int``):
Total number of profile pictures the target user has.
profile_photos (List of :obj:`Photo`):
Requested profile pictures.
"""
__slots__ = ["total_count", "profile_photos"]
def __init__(
self,
*,
client: "pyrogram.BaseClient" = None,
total_count: int,
profile_photos: List[Photo]
):
super().__init__(client)
self.total_count = total_count
self.profile_photos = profile_photos
@staticmethod
def _parse(client, photos) -> "ProfilePhotos":
return ProfilePhotos(
total_count=getattr(photos, "count", len(photos.photos)),
profile_photos=[Photo._parse(client, photo) for photo in photos.photos],
client=client
)

View File

@ -18,16 +18,13 @@
from .chat import Chat from .chat import Chat
from .chat_member import ChatMember from .chat_member import ChatMember
from .chat_members import ChatMembers
from .chat_permissions import ChatPermissions from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto from .chat_photo import ChatPhoto
from .chat_preview import ChatPreview from .chat_preview import ChatPreview
from .dialog import Dialog from .dialog import Dialog
from .dialogs import Dialogs
from .user import User from .user import User
from .user_status import UserStatus from .user_status import UserStatus
__all__ = [ __all__ = [
"Chat", "ChatMember", "ChatMembers", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "Dialogs", "User", "Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User", "UserStatus"
"UserStatus"
] ]

View File

@ -1,71 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from typing import List
import pyrogram
from pyrogram.api import types
from .chat_member import ChatMember
from ..object import Object
class ChatMembers(Object):
"""Contains information about the members list of a chat.
Parameters:
total_count (``int``):
Total number of members the chat has.
chat_members (List of :obj:`ChatMember <pyrogram.ChatMember>`):
Requested chat members.
"""
__slots__ = ["total_count", "chat_members"]
def __init__(
self,
*,
client: "pyrogram.BaseClient" = None,
total_count: int,
chat_members: List[ChatMember]
):
super().__init__(client)
self.total_count = total_count
self.chat_members = chat_members
@staticmethod
def _parse(client, members):
users = {i.id: i for i in members.users}
chat_members = []
if isinstance(members, types.channels.ChannelParticipants):
total_count = members.count
members = members.participants
else:
members = members.full_chat.participants.participants
total_count = len(members)
for member in members:
chat_members.append(ChatMember._parse(client, member, users))
return ChatMembers(
total_count=total_count,
chat_members=chat_members,
client=client
)

View File

@ -1,87 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from typing import List
import pyrogram
from pyrogram.api import types
from .dialog import Dialog
from ..messages_and_media import Message
from ..object import Object
class Dialogs(Object):
"""Contains a user's dialogs chunk.
Parameters:
total_count (``int``):
Total number of dialogs the user has.
dialogs (List of :obj:`Dialog`):
Requested dialogs.
"""
__slots__ = ["total_count", "dialogs"]
def __init__(
self,
*,
client: "pyrogram.BaseClient" = None,
total_count: int,
dialogs: List[Dialog]
):
super().__init__(client)
self.total_count = total_count
self.dialogs = dialogs
@staticmethod
def _parse(client, dialogs: types.messages.Dialogs) -> "Dialogs":
users = {i.id: i for i in dialogs.users}
chats = {i.id: i for i in dialogs.chats}
messages = {}
for message in dialogs.messages:
to_id = message.to_id
if isinstance(to_id, types.PeerUser):
if message.out:
chat_id = to_id.user_id
else:
chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else:
chat_id = int("-100" + str(to_id.channel_id))
messages[chat_id] = Message._parse(client, message, users, chats)
parsed_dialogs = []
for dialog in dialogs.dialogs:
if not isinstance(dialog, types.Dialog):
continue
parsed_dialogs.append(Dialog._parse(client, dialog, messages, users, chats))
return Dialogs(
total_count=getattr(dialogs, "count", len(dialogs.dialogs)),
dialogs=parsed_dialogs,
client=client
)