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
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
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
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
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