Exemple #1
0
    async def get_messages(self, *args, **kwargs):
        """
        Same as :meth:`iter_messages`, but returns a list instead
        with an additional ``.total`` attribute on the list.

        If the `limit` is not set, it will be 1 by default unless both
        `min_id` **and** `max_id` are set (as *named* arguments), in
        which case the entire range will be returned.

        This is so because any integer limit would be rather arbitrary and
        it's common to only want to fetch one message, but if a range is
        specified it makes sense that it should return the entirety of it.

        If `ids` is present in the *named* arguments and is not a list,
        a single :tl:`Message` will be returned for convenience instead
        of a list.
        """
        total = [0]
        kwargs['_total'] = total
        if len(args) == 1 and 'limit' not in kwargs:
            if 'min_id' in kwargs and 'max_id' in kwargs:
                kwargs['limit'] = None
            else:
                kwargs['limit'] = 1

        msgs = UserList()
        async for x in self.iter_messages(*args, **kwargs):
            msgs.append(x)
        msgs.total = total[0]
        if 'ids' in kwargs and not utils.is_list_like(kwargs['ids']):
            return msgs[0]

        return msgs
Exemple #2
0
    async def get_messages(self, *args, **kwargs):
        """
        Same as :meth:`iter_messages`, but returns a list instead
        with an additional ``.total`` attribute on the list.

        If the `limit` is not set, it will be 1 by default unless both
        `min_id` **and** `max_id` are set (as *named* arguments), in
        which case the entire range will be returned.

        This is so because any integer limit would be rather arbitrary and
        it's common to only want to fetch one message, but if a range is
        specified it makes sense that it should return the entirety of it.

        If `ids` is present in the *named* arguments and is not a list,
        a single :tl:`Message` will be returned for convenience instead
        of a list.
        """
        total = [0]
        kwargs['_total'] = total
        if len(args) == 1 and 'limit' not in kwargs:
            if 'min_id' in kwargs and 'max_id' in kwargs:
                kwargs['limit'] = None
            else:
                kwargs['limit'] = 1

        msgs = UserList()
        async for x in self.iter_messages(*args, **kwargs):
            msgs.append(x)
        msgs.total = total[0]
        if 'ids' in kwargs and not utils.is_list_like(kwargs['ids']):
            return msgs[0]

        return msgs
Exemple #3
0
 async def get_dialogs(self, *args, **kwargs):
     """
     Same as :meth:`iter_dialogs`, but returns a list instead
     with an additional ``.total`` attribute on the list.
     """
     total = [0]
     kwargs['_total'] = total
     dialogs = UserList()
     async for x in self.iter_dialogs(*args, **kwargs):
         dialogs.append(x)
     dialogs.total = total[0]
     return dialogs
Exemple #4
0
 async def get_dialogs(self, *args, **kwargs):
     """
     Same as :meth:`iter_dialogs`, but returns a list instead
     with an additional ``.total`` attribute on the list.
     """
     total = [0]
     kwargs['_total'] = total
     dialogs = UserList()
     async for x in self.iter_dialogs(*args, **kwargs):
         dialogs.append(x)
     dialogs.total = total[0]
     return dialogs
Exemple #5
0
 async def get_participants(self, *args, **kwargs):
     """
     Same as :meth:`iter_participants`, but returns a list instead
     with an additional ``.total`` attribute on the list.
     """
     total = [0]
     kwargs['_total'] = total
     participants = UserList()
     async for x in self.iter_participants(*args, **kwargs):
         participants.append(x)
     participants.total = total[0]
     return participants
Exemple #6
0
    def get_message_history(self,
                            entity,
                            limit=20,
                            offset_date=None,
                            offset_id=0,
                            max_id=0,
                            min_id=0,
                            add_offset=0):
        """
        Gets the message history for the specified entity

        Args:
            entity (:obj:`entity`):
                The entity from whom to retrieve the message history.

            limit (:obj:`int` | :obj:`None`, optional):
                Number of messages to be retrieved. Due to limitations with
                the API retrieving more than 3000 messages will take longer
                than half a minute (or even more based on previous calls).
                The limit may also be ``None``, which would eventually return
                the whole history.

            offset_date (:obj:`datetime`):
                Offset date (messages *previous* to this date will be
                retrieved). Exclusive.

            offset_id (:obj:`int`):
                Offset message ID (only messages *previous* to the given
                ID will be retrieved). Exclusive.

            max_id (:obj:`int`):
                All the messages with a higher (newer) ID or equal to this will
                be excluded

            min_id (:obj:`int`):
                All the messages with a lower (older) ID or equal to this will
                be excluded.

            add_offset (:obj:`int`):
                Additional message offset (all of the specified offsets +
                this offset = older messages).

        Returns:
            A list of messages with extra attributes:

                * ``.total`` = (on the list) total amount of messages sent.
                * ``.sender`` = entity of the sender.
                * ``.fwd_from.sender`` = if fwd_from, who sent it originally.
                * ``.fwd_from.channel`` = if fwd_from, original channel.
                * ``.to`` = entity to which the message was sent.
        """
        entity = self.get_input_entity(entity)
        limit = float('inf') if limit is None else int(limit)
        if limit == 0:
            # No messages, but we still need to know the total message count
            result = self(
                GetHistoryRequest(peer=entity,
                                  limit=1,
                                  offset_date=None,
                                  offset_id=0,
                                  max_id=0,
                                  min_id=0,
                                  add_offset=0))
            return getattr(result, 'count', len(result.messages)), [], []

        total_messages = 0
        messages = UserList()
        entities = {}
        while len(messages) < limit:
            # Telegram has a hard limit of 100
            real_limit = min(limit - len(messages), 100)
            result = self(
                GetHistoryRequest(peer=entity,
                                  limit=real_limit,
                                  offset_date=offset_date,
                                  offset_id=offset_id,
                                  max_id=max_id,
                                  min_id=min_id,
                                  add_offset=add_offset,
                                  hash=0))
            messages.extend(m for m in result.messages
                            if not isinstance(m, MessageEmpty))
            total_messages = getattr(result, 'count', len(result.messages))

            # TODO We can potentially use self.session.database, but since
            # it might be disabled, use a local dictionary.
            for u in result.users:
                entities[utils.get_peer_id(u)] = u
            for c in result.chats:
                entities[utils.get_peer_id(c)] = c

            if len(result.messages) < real_limit:
                break

            offset_id = result.messages[-1].id
            offset_date = result.messages[-1].date

            # Telegram limit seems to be 3000 messages within 30 seconds in
            # batches of 100 messages each request (since the FloodWait was
            # of 30 seconds). If the limit is greater than that, we will
            # sleep 1s between each request.
            if limit > 3000:
                time.sleep(1)

        # Add a few extra attributes to the Message to make it friendlier.
        messages.total = total_messages
        for m in messages:
            # TODO Better way to return a total without tuples?
            m.sender = (None if not m.from_id else entities[utils.get_peer_id(
                m.from_id)])

            if getattr(m, 'fwd_from', None):
                m.fwd_from.sender = (None if not m.fwd_from.from_id else
                                     entities[utils.get_peer_id(
                                         m.fwd_from.from_id)])
                m.fwd_from.channel = (None if not m.fwd_from.channel_id else
                                      entities[utils.get_peer_id(
                                          PeerChannel(m.fwd_from.channel_id))])

            m.to = entities[utils.get_peer_id(m.to_id)]

        return messages
Exemple #7
0
    def get_dialogs(self,
                    limit=10,
                    offset_date=None,
                    offset_id=0,
                    offset_peer=InputPeerEmpty()):
        """
        Gets N "dialogs" (open "chats" or conversations with other people).

        Args:
            limit (:obj:`int` | :obj:`None`):
                How many dialogs to be retrieved as maximum. Can be set to
                ``None`` to retrieve all dialogs. Note that this may take
                whole minutes if you have hundreds of dialogs, as Telegram
                will tell the library to slow down through a
                ``FloodWaitError``.

            offset_date (:obj:`datetime`, optional):
                The offset date to be used.

            offset_id (:obj:`int`, optional):
                The message ID to be used as an offset.

            offset_peer (:obj:`InputPeer`, optional):
                The peer to be used as an offset.

        Returns:
            A list dialogs, with an additional .total attribute on the list.
        """
        limit = float('inf') if limit is None else int(limit)
        if limit == 0:
            # Special case, get a single dialog and determine count
            dialogs = self(
                GetDialogsRequest(offset_date=offset_date,
                                  offset_id=offset_id,
                                  offset_peer=offset_peer,
                                  limit=1))
            result = UserList()
            result.total = getattr(dialogs, 'count', len(dialogs.dialogs))
            return result

        total_count = 0
        dialogs = OrderedDict()  # Use peer id as identifier to avoid dupes
        while len(dialogs) < limit:
            real_limit = min(limit - len(dialogs), 100)
            r = self(
                GetDialogsRequest(offset_date=offset_date,
                                  offset_id=offset_id,
                                  offset_peer=offset_peer,
                                  limit=real_limit))

            total_count = getattr(r, 'count', len(r.dialogs))
            messages = {m.id: m for m in r.messages}
            entities = {
                utils.get_peer_id(x): x
                for x in itertools.chain(r.users, r.chats)
            }

            for d in r.dialogs:
                dialogs[utils.get_peer_id(d.peer)] = \
                    Dialog(self, d, entities, messages)

            if len(r.dialogs) < real_limit or not isinstance(r, DialogsSlice):
                # Less than we requested means we reached the end, or
                # we didn't get a DialogsSlice which means we got all.
                break

            offset_date = r.messages[-1].date
            offset_peer = entities[utils.get_peer_id(r.dialogs[-1].peer)]
            offset_id = r.messages[-1].id & 4294967296  # Telegram/danog magic

        dialogs = UserList(
            itertools.islice(dialogs.values(), min(limit, len(dialogs))))
        dialogs.total = total_count
        return dialogs