Esempio n. 1
0
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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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]
Esempio n. 5
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
Esempio n. 7
0
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)
Esempio n. 8
0
    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
        )
Esempio n. 9
0
    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)
Esempio n. 10
0
    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
        )
Esempio n. 11
0
    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)
Esempio n. 12
0
    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]
Esempio n. 13
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)
Esempio n. 14
0
    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))
Esempio n. 15
0
    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)
Esempio n. 16
0
    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]
Esempio n. 17
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))