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 get_profile_photos(self, chat_id: Union[int, str], offset: int = 0, limit: int = 100) -> List["pyrogram.Photo"]: """Get a list of profile pictures for a user or a chat. 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). offset (``int``, *optional*): Sequential number of the first photo to be returned. By default, all photos are returned. limit (``int``, *optional*): Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. Returns: List of :obj:`Photo`: On success, a list of profile photos is returned. Raises: RPCError: In case of a Telegram RPC error. """ peer_id = self.resolve_peer(chat_id) if isinstance(peer_id, types.InputPeerChannel): r = utils.parse_messages( self, self.send( functions.messages.Search( peer=peer_id, q="", filter=types.InputMessagesFilterChatPhotos(), min_date=0, max_date=0, offset_id=0, add_offset=offset, limit=limit, max_id=0, min_id=0, hash=0))) return pyrogram.List([message.new_chat_photo for message in r][:limit]) else: r = self.send( functions.photos.GetUserPhotos(user_id=peer_id, offset=offset, max_id=0, limit=limit)) return pyrogram.List( pyrogram.Photo._parse(self, photo) for photo in r.photos)
def _parse(client, user: types.User) -> "User" or None: if user is None: return None return User( id=user.id, is_self=user.is_self, is_contact=user.contact, is_mutual_contact=user.mutual_contact, is_deleted=user.deleted, is_bot=user.bot, is_verified=user.verified, is_restricted=user.restricted, is_scam=user.scam, is_support=user.support, first_name=user.first_name, last_name=user.last_name, **User._parse_status(user.status, user.bot), username=user.username, language_code=user.lang_code, dc_id=getattr(user.photo, "dc_id", None), phone_number=user.phone, photo=ChatPhoto._parse(client, user.photo, user.id, user.access_hash), restrictions=pyrogram.List( [Restriction._parse(r) for r in user.restriction_reason]) or None, client=client)
async def get_users( self, user_ids: Union[Iterable[Union[int, str]], int, str] ) -> Union["pyrogram.User", List["pyrogram.User"]]: """Get information about a user. You can retrieve up to 200 users at once. Parameters: user_ids (``iterable``): A list of User identifiers (id or username) or a single user id/username. For a contact that exists in your Telegram address book you can use his phone number (str). Iterators and Generators are also accepted. Returns: :obj:`User` | List of :obj:`User`: In case *user_ids* was an integer or string the single requested user is returned, otherwise, in case *user_ids* was an iterable a list of users is returned, even if the iterable contained one item only. Raises: RPCError: In case of a Telegram RPC error. """ is_iterable = not isinstance(user_ids, (int, str)) user_ids = list(user_ids) if is_iterable else [user_ids] user_ids = await asyncio.gather( *[self.resolve_peer(i) for i in user_ids]) r = await self.send(functions.users.GetUsers(id=user_ids)) users = pyrogram.List() for i in r: users.append(pyrogram.User._parse(self, i)) return users if is_iterable else users[0]
async def get_contacts(self) -> List["pyrogram.User"]: """Get contacts from your Telegram address book. Returns: List of :obj:`User`: On success, a list of users is returned. Example: .. code-block:: python contacts = app.get_contacts() print(contacts) """ contacts = await self.send(functions.contacts.GetContacts(hash=0)) return pyrogram.List( pyrogram.User._parse(self, user) for user in contacts.users)
async def get_nearby_chats( self, latitude: float, longitude: float ) -> List["pyrogram.Chat"]: """Get nearby chats. Parameters: latitude (``float``): Latitude of the location. longitude (``float``): Longitude of the location. Returns: List of :obj:`Chat`: On success, a list of nearby chats is returned. Example: .. code-block:: python chats = app.get_nearby_chats(51.500729, -0.124583) print(chats) """ r = await self.send( functions.contacts.GetLocated( geo_point=types.InputGeoPoint( lat=latitude, long=longitude ) ) ) if not r.updates: return [] chats = pyrogram.List([pyrogram.Chat._parse_chat(self, chat) for chat in r.chats]) peers = r.updates[0].peers for peer in peers: chat_id = utils.get_channel_id(peer.peer.channel_id) for chat in chats: if chat.id == chat_id: chat.distance = peer.distance break return chats
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=get_channel_id(channel_id), type="channel", client=client) if channel_id is not None else None, client=client)) return pyrogram.List(parsed_messages)
def _parse_user_chat(client, user: types.User) -> "Chat": peer_id = user.id return Chat( id=peer_id, type="bot" if user.bot else "private", is_verified=getattr(user, "verified", None), is_restricted=getattr(user, "restricted", None), is_scam=getattr(user, "scam", None), is_support=getattr(user, "support", None), username=user.username, first_name=user.first_name, last_name=user.last_name, photo=ChatPhoto._parse(client, user.photo, peer_id, user.access_hash), restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None, client=client )
def get_game_high_scores( self, user_id: Union[int, str], chat_id: Union[int, str], message_id: int = None ) -> List["pyrogram.GameHighScore"]: """Get data for high score tables. Parameters: user_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). chat_id (``int`` | ``str``, *optional*): 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). Required if inline_message_id is not specified. message_id (``int``, *optional*): Identifier of the sent message. Required if inline_message_id is not specified. Returns: List of :obj:`GameHighScore`: On success. Example: .. code-block:: python scores = app.get_game_high_scores(user_id, chat_id, message_id) print(scores) """ # TODO: inline_message_id r = self.send( functions.messages.GetGameHighScores( peer=self.resolve_peer(chat_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)
def _parse_channel_chat(client, channel: types.Channel) -> "Chat": peer_id = utils.get_channel_id(channel.id) restriction_reason = getattr(channel, "restriction_reason", []) return Chat( id=peer_id, type="supergroup" if channel.megagroup else "channel", is_verified=getattr(channel, "verified", None), is_restricted=getattr(channel, "restricted", None), is_creator=getattr(channel, "creator", None), is_scam=getattr(channel, "scam", None), title=channel.title, username=getattr(channel, "username", None), photo=ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id, channel.access_hash), restrictions=pyrogram.List([Restriction._parse(r) for r in restriction_reason]) or None, permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), members_count=getattr(channel, "participants_count", None), client=client )
def get_contacts(self) -> List["pyrogram.User"]: # TODO: Create a Users object and return that """Get contacts from your Telegram address book. Returns: List of :obj:`User`: On success, a list of users is returned. Raises: RPCError: In case of a Telegram RPC error. """ while True: try: contacts = self.send(functions.contacts.GetContacts(hash=0)) except FloodWait as e: log.warning("get_contacts flood: waiting {} seconds".format( e.x)) time.sleep(e.x) else: return pyrogram.List( pyrogram.User._parse(self, user) for user in contacts.users)
def get_users( self, user_ids: Union[Iterable[Union[int, str]], int, str] ) -> Union["pyrogram.User", List["pyrogram.User"]]: """Get information about a user. You can retrieve up to 200 users at once. Parameters: user_ids (``iterable``): A list of User identifiers (id or username) or a single user id/username. For a contact that exists in your Telegram address book you can use his phone number (str). Iterators and Generators are also accepted. Returns: :obj:`User` | List of :obj:`User`: In case *user_ids* was an integer or string the single requested user is returned, otherwise, in case *user_ids* was an iterable a list of users is returned, even if the iterable contained one item only. Example: .. code-block:: python # Get information about one user app.get_users("haskell") # Get information about multiple users at once app.get_users([user1, user2, user3]) """ is_iterable = not isinstance(user_ids, (int, str)) user_ids = list(user_ids) if is_iterable else [user_ids] user_ids = [self.resolve_peer(i) for i in user_ids] r = self.send(functions.users.GetUsers(id=user_ids)) users = pyrogram.List() for i in r: users.append(pyrogram.User._parse(self, i)) return users if is_iterable else users[0]
def get_contacts(self) -> List["pyrogram.User"]: """Get contacts from your Telegram address book. Returns: List of :obj:`User`: On success, a list of users is returned. Example: .. code-block:: python contacts = app.get_contacts() print(contacts) """ while True: try: contacts = self.send(functions.contacts.GetContacts(hash=0)) except FloodWait as e: log.warning("get_contacts flood: waiting {} seconds".format( e.x)) time.sleep(e.x) else: return pyrogram.List( pyrogram.User._parse(self, user) for user in contacts.users)
def get_common_chats(self, user_id: Union[int, str]) -> list: """Get the common chats you have with a user. Parameters: user_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). Returns: List of :obj:`Chat`: On success, a list of the common chats is returned. Raises: ValueError: If the user_id doesn't belong to a user. Example: .. code-block:: python common = app.get_common_chats("haskell") print(common) """ peer = self.resolve_peer(user_id) if isinstance(peer, types.InputPeerUser): r = self.send( functions.messages.GetCommonChats( user_id=peer, max_id=0, limit=100, )) return pyrogram.List( [pyrogram.Chat._parse_chat(self, x) for x in r.chats]) raise ValueError( 'The user_id "{}" doesn\'t belong to a user'.format(user_id))
async def get_dialogs( self, offset_date: int = 0, limit: int = 100, pinned_only: bool = False) -> List["pyrogram.Dialog"]: """Get a chunk of the user's dialogs. You can get up to 100 dialogs at once. For a more convenient way of getting a user's dialogs see :meth:`~Client.iter_dialogs`. Parameters: offset_date (``int``): The offset date in Unix time taken from the top message of a :obj:`Dialog`. Defaults to 0. Valid for non-pinned dialogs only. limit (``str``, *optional*): Limits the number of dialogs to be retrieved. Defaults to 100. Valid for non-pinned dialogs only. pinned_only (``bool``, *optional*): Pass True if you want to get only pinned dialogs. Defaults to False. Returns: List of :obj:`Dialog`: On success, a list of dialogs is returned. Example: .. code-block:: python # Get first 100 dialogs app.get_dialogs() # Get pinned dialogs app.get_dialogs(pinned_only=True) """ while True: try: if pinned_only: r = await self.send( functions.messages.GetPinnedDialogs(folder_id=0)) else: r = await self.send( functions.messages.GetDialogs( offset_date=offset_date, offset_id=0, offset_peer=types.InputPeerEmpty(), limit=limit, hash=0, exclude_pinned=True)) except FloodWait as e: log.warning("Sleeping for {}s".format(e.x)) await asyncio.sleep(e.x) else: break 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 else: chat_id = utils.get_peer_id(to_id) messages[chat_id] = await 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)
async def forward_messages( self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_ids: Union[int, Iterable[int]], disable_notification: bool = None, as_copy: bool = False, remove_caption: bool = False, schedule_date: int = None) -> List["pyrogram.Message"]: """Forward messages of any kind. 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). from_chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the source chat where the original message was sent. 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). message_ids (``iterable``): A list of Message identifiers in the chat specified in *from_chat_id* or a single message id. Iterators and Generators are also accepted. disable_notification (``bool``, *optional*): Sends the message 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 so that it appears as originally sent by you). 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. Returns: :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. Example: .. code-block:: python :emphasize-lines: 2,5,8 # Forward a single message app.forward_messages("me", "pyrogram", 20) # Forward multiple messages at once app.forward_messages("me", "pyrogram", [3, 20, 27]) # Forward messages as copy app.forward_messages("me", "pyrogram", 20, as_copy=True) """ is_iterable = not isinstance(message_ids, int) message_ids = list(message_ids) if is_iterable else [message_ids] if as_copy: forwarded_messages = [] for chunk in [ message_ids[i:i + 200] for i in range(0, len(message_ids), 200) ]: messages = await self.get_messages(chat_id=from_chat_id, message_ids=chunk) for message in messages: forwarded_messages.append(await message.forward( chat_id, disable_notification=disable_notification, as_copy=True, remove_caption=remove_caption, schedule_date=schedule_date)) return pyrogram.List( forwarded_messages) if is_iterable else forwarded_messages[0] else: r = await self.send( functions.messages.ForwardMessages( to_peer=await self.resolve_peer(chat_id), from_peer=await self.resolve_peer(from_chat_id), id=message_ids, silent=disable_notification or None, random_id=[self.rnd_id() for _ in message_ids], schedule_date=schedule_date)) forwarded_messages = [] users = {i.id: i for i in r.users} chats = {i.id: i for i in r.chats} for i in r.updates: if isinstance( i, (types.UpdateNewMessage, types.UpdateNewChannelMessage, types.UpdateNewScheduledMessage)): forwarded_messages.append(await pyrogram.Message._parse( self, i.message, users, chats)) return pyrogram.List( forwarded_messages) if is_iterable else forwarded_messages[0]
def get_chat_members( self, chat_id: Union[int, str], offset: int = 0, limit: int = 200, query: str = "", filter: str = Filters.ALL) -> List["pyrogram.ChatMember"]: """Get a chunk of the members list of a chat. You can get up to 200 chat members at once. A chat can be either a basic group, a supergroup or a channel. You must be admin to retrieve the members list of a channel (also known as "subscribers"). For a more convenient way of getting chat members see :meth:`~Client.iter_chat_members`. Parameters: chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target chat. offset (``int``, *optional*): Sequential number of the first member to be returned. Only applicable to supergroups and channels. Defaults to 0 [1]_. limit (``int``, *optional*): 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. query (``str``, *optional*): Query string to filter members based on their display names and usernames. Only applicable to supergroups and channels. Defaults to "" (empty string) [2]_. filter (``str``, *optional*): Filter used to select the kind of members you want to retrieve. Only applicable for supergroups and channels. It can be any of the followings: *"all"* - all kind of members, *"kicked"* - kicked (banned) members only, *"restricted"* - restricted members only, *"bots"* - bots only, *"recent"* - recent members only, *"administrators"* - chat administrators only. Only applicable to supergroups and channels. 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 on channels. .. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only. Returns: List of :obj:`ChatMember`: On success, a list of chat members is returned. Raises: ValueError: In case you used an invalid filter or a chat id that belongs to a user. Example: .. code-block:: python # Get first 200 recent members app.get_chat_members("pyrogramchat") # Get all administrators app.get_chat_members("pyrogramchat", filter="administrators") # Get all bots app.get_chat_members("pyrogramchat", filter="bots") """ peer = self.resolve_peer(chat_id) if isinstance(peer, types.InputPeerChat): r = self.send(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): filter = filter.lower() if filter == Filters.ALL: filter = types.ChannelParticipantsSearch(q=query) elif filter == Filters.KICKED: filter = types.ChannelParticipantsKicked(q=query) elif filter == Filters.RESTRICTED: filter = types.ChannelParticipantsBanned(q=query) elif filter == Filters.BOTS: filter = types.ChannelParticipantsBots() elif filter == Filters.RECENT: filter = types.ChannelParticipantsRecent() elif filter == Filters.ADMINISTRATORS: filter = types.ChannelParticipantsAdmins() else: raise ValueError("Invalid filter \"{}\"".format(filter)) while True: try: r = self.send( functions.channels.GetParticipants(channel=peer, filter=filter, offset=offset, 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: log.warning("Sleeping for {}s".format(e.x)) time.sleep(e.x) else: raise ValueError( "The chat_id \"{}\" belongs to a user".format(chat_id))