U
    hTd                     @   s   d dl Z d dlZd dlZd dlZd dlZddlmZmZmZm	Z	 ddlm
Z
mZ ddlmZ ddlmZmZmZ dd Zejrd	d
lmZ dejdddZG dd dZdS )    N   )errorshelpersutilshints)
MultiErrorRPCError)retry_range)	TLRequesttypes	functionsc                   C   s   t dS )Nz(You can only invoke requests, not types!)	TypeError r   r   9/tmp/pip-unpacked-wheel-c81u5j2r/telethon/client/users.py<lambda>       r      )TelegramClientF)earlytdc                C   s    d|r
dnd| || d|j jfS )Nz(Sleeping%s for %ds (%s) on %s flood waitz early )seconds)	__class____name__)delayrequestr   r   r   r   r   
_fmt_flood   s    
r   c                   @   s   e Zd Zd+ddddZd,ddddZd-ded	d
ddZedej	e
 dddZdedddZdedddZddejdejd f dddZddddddZddddd Zd.ddee
d"d#d$Zddd%d&Zddd'd(Zddd)d*ZdS )/UserMethodsFNr   selfc                    s   | j | j||dI d H S )Nordered)_callZ_sender)r   r   r!   flood_sleep_thresholdr   r   r   __call__   s    zUserMethods.__call__c                    sH  | j d k	r | j t kr td|d kr.| j}t|r@t|n|g}t|rXt|n|}t|D ]\}}t	|t
s|t || tI d H  |j| jkr&| j|j }t|t  }	|	dkr| j|jd  nT|	|kr| jt jt|	|dd  t|	I d H  | j|jd  ntj||	d| jrdt|rHt|||< qdt|}qdd}
d }t | _t| jD ]}z|j ||d}t	|trNg }g }|D ]}z|I d H }W nB t!k
r } z"|"| |"d  W Y qW 5 d }~X Y nX | j#$| |"d  |"| |
d7 }
qt%d	d
 |D rBt&|||n
|W   S n |I d H }| j#$| |W   S W qp tj'tj(tj)tj*tj+tj,fk
r } z0|}| jt -d|j.j| tdI d H  W 5 d }~X Y qp tjtj/tj0tj1fk
r } z|}t|r||
 }t	|tj0s:t |j2 | j|j< |j2dkrLd|_2|j2| jkr| jt jt|j2|  t|j2I d H  n W 5 d }~X Y n tj3tj4tj5fk
r } zX|}| jt d|j6 t	|tj3tj4f}|r| 7 I d H r | 8|j6I d H  W 5 d }~X Y nX qp| j9r6|d k	r6|t:d;|d S )NzQThe asyncio event loop must not change after connection (see the FAQ for details)   T)r   )r   capturer   r    r   c                 s   s   | ]}|d k	V  qd S Nr   .0xr   r   r   	<genexpr>W   s     z$UserMethods._call.<locals>.<genexpr>z)Telegram is having internal issues %s: %sr   zPhone migrated to %dz#Request was unsuccessful {} time(s))<Z_loopr   Zget_running_loopRuntimeErrorr#   r   is_list_likelist	enumerate
isinstancer
   _NOT_A_REQUESTresolveZCONSTRUCTOR_IDZ_flood_waited_requestsroundtimepop_logr   infor   asynciosleepr   ZFloodWaitErrorZ_no_updatesr   ZInvokeWithoutUpdatesRequestZ_last_requestr	   Z_request_retriessendr   appendsessionZprocess_entitiesanyr   ZServerErrorZRpcCallFailErrorZRpcMcgetFailErrorZInterdcCallErrorErrorZTimedOutErrorZInterdcCallRichErrorErrorwarningr   ZFloodPremiumWaitErrorZSlowModeWaitErrorZFloodTestPhoneWaitErrorr   ZPhoneMigrateErrorZNetworkMigrateErrorZUserMigrateErrorZnew_dcis_user_authorizedZ
_switch_dcZ_raise_last_call_error
ValueErrorformat)r   Zsenderr   r!   r#   requestsirZdueZdiffZrequest_indexZ
last_errorattemptfutureresults
exceptionsfresulteZshould_raiser   r   r   r"       s    








 
 "
 
 (zUserMethods._callz-typing.Union[types.User, types.InputPeerUser])r   
input_peerreturnc                    s   |r | j jr | j | j j S zV| tjt gI dH d }| j js`| j 	|j
|j|j |rrtj|ddn|W S  tjk
r   Y dS X dS )a<  
        Gets "me", the current :tl:`User` who is logged in.

        If the user has not logged in yet, this method returns `None`.

        Arguments
            input_peer (`bool`, optional):
                Whether to return the :tl:`InputPeerUser` version or the normal
                :tl:`User`. This can be useful if you just need to know the ID
                of yourself.

        Returns
            Your own :tl:`User`.

        Example
            .. code-block:: python

                me = await client.get_me()
                print(me.username)
        Nr   F)Z
allow_self)_mb_entity_cacheself_idget_as_input_peerr   usersGetUsersRequestr   ZInputUserSelfZset_self_useridZbotaccess_hashr   get_input_peerr   ZUnauthorizedError)r   rL   mer   r   r   get_me   s    zUserMethods.get_me)r   rM   c                 C   s   | j jS )z
        Returns the ID of the logged-in user, if known.

        This property is used in every update, and some like `updateLoginToken`
        occur prior to login, so it gracefully handles when no ID is known yet.
        )rN   rO   r   r   r   r   _self_id   s    zUserMethods._self_idc                    s&   | j jdkr| jddI dH  | j jS )a  
        Return `True` if the signed-in user is a bot, `False` otherwise.

        Example
            .. code-block:: python

                if await client.is_bot():
                    print('Beep')
                else:
                    print('Hello')
        NTrL   )rN   Zself_botrX   r   r   r   r   is_bot   s    zUserMethods.is_botc                    sL   | j dkrFz| tj I dH  d| _ W n tjk
rD   d| _ Y nX | j S )aW  
        Returns `True` if the user is authorized (logged in).

        Example
            .. code-block:: python

                if not await client.is_user_authorized():
                    await client.send_code_request(phone)
                    code = input('enter code: ')
                    await client.sign_in(phone, code)
        NTF)Z_authorizedr   ZupdatesZGetStateRequestr   r   r   r   r   r   r?      s    

zUserMethods.is_user_authorizedzhints.EntitiesLikezhints.Entity)r   entityrM   c              	      s  t | }|r|f}g }|D ]0}t|tr8|| q|| |I dH  qtjjg tjj	g tjj
g i}|D ]2}z|t| | W qp tk
r   Y qpX qp|tjj }|tjj	 }|tjj
 }|rg }	|r|dd |dd  }
}|	| tj|
I dH  q|	}|r<| tjdd |D I dH j}|rZ| tj|I dH j}dd t|||D }g }|D ]n}t|tr|| |I dH  nDt|tjs||t j|dd  n|td	d
 | D  qz|r|d S |S )a	  
        Turns the given entity into a valid Telegram :tl:`User`, :tl:`Chat`
        or :tl:`Channel`. You can also pass a list or iterable of entities,
        and they will be efficiently fetched from the network.

        Arguments
            entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`):
                If a username is given, **the username will be resolved** making
                an API call every time. Resolving usernames is an expensive
                operation and will start hitting flood waits around 50 usernames
                in a short period of time.

                If you want to get the entity for a *cached* username, you should
                first `get_input_entity(username) <get_input_entity>` which will
                use the cache), and then use `get_entity` with the result of the
                previous call.

                Similar limits apply to invite links, and you should use their
                ID instead.

                Using phone numbers (from people in your contact list), exact
                names, integer IDs or :tl:`Peer` rely on a `get_input_entity`
                first, which in turn needs the entity to be in cache, unless
                a :tl:`InputPeer` was passed.

                Unsupported types will raise ``TypeError``.

                If the entity can't be found, ``ValueError`` will be raised.

        Returns
            :tl:`User`, :tl:`Chat` or :tl:`Channel` corresponding to the
            input entity. A list will be returned if more than one was given.

        Example
            .. code-block:: python

                from telethon import utils

                me = await client.get_entity('me')
                print(utils.get_display_name(me))

                chat = await client.get_input_entity('username')
                async for message in client.iter_messages(chat):
                    ...

                # Note that you could have used the username directly, but it's
                # good to use get_input_entity if you will reuse it a lot.
                async for message in client.iter_messages('username'):
                    ...

                # Note that for this to work the phone number must be in your contacts
                some_id = await client.get_peer_id('+34123456789')
        N   c                 S   s   g | ]
}|j qS r   )chat_idr(   r   r   r   
<listcomp>C  s     z*UserMethods.get_entity.<locals>.<listcomp>c                 S   s   i | ]}t j|d d|qS )Fadd_mark)r   get_peer_idr(   r   r   r   
<dictcomp>I  s    z*UserMethods.get_entity.<locals>.<dictcomp>Fr`   c                 s   s$   | ]}t |tjr|jr|V  qd S r'   )r0   r   ZUserZis_self)r)   ur   r   r   r+   [  s    z)UserMethods.get_entity.<locals>.<genexpr>r   )r   r-   r0   strr;   get_input_entityr   Z_EntityTypeUSERZCHATZCHANNELZ_entity_typer   extendr   rR   rS   messagesZGetChatsRequestchatschannelsGetChannelsRequest	itertoolschain_get_entity_from_stringr   InputPeerSelfrb   nextvalues)r   r\   Zsingleinputsr*   listsrR   rj   rk   tmpcurrZ	id_entityrJ   r   r   r   
get_entity   sd    8
   
zUserMethods.get_entityzhints.EntityLikeztypes.TypeInputPeer)r   peerrM   c                    s  zt |W S  tk
r    Y nX z4t|ts8|jdkrT| jt j|dd	 W S W n t
k
rj   Y nX |dkr|t S z| j|W S  tk
r   Y nX t|trt | |I dH S t |}t|tjr$| tjtj|jddgI dH }|rt|d tjst |d S nxt|tjr>t|jS t|tjrz6| tjtj |j!ddgI dH }t |j"d W S  t#j$k
r   Y nX td%|t&|j'dS )	a(  
        Turns the given entity into its input entity version.

        Most requests use this kind of :tl:`InputPeer`, so this is the most
        suitable call to make for those cases. **Generally you should let the
        library do its job** and don't worry about getting the input entity
        first, but if you're going to use an entity often, consider making the
        call:

        Arguments
            entity (`str` | `int` | :tl:`Peer` | :tl:`InputPeer`):
                If a username or invite link is given, **the library will
                use the cache**. This means that it's possible to be using
                a username that *changed* or an old invite link (this only
                happens if an invite link for a small group chat is used
                after it was upgraded to a mega-group).

                If the username or ID from the invite link is not found in
                the cache, it will be fetched. The same rules apply to phone
                numbers (``'+34 123456789'``) from people in your contact list.

                If an exact name is given, it must be in the cache too. This
                is not reliable as different people can share the same name
                and which entity is returned is arbitrary, and should be used
                only for quick tests.

                If a positive integer ID is given, the entity will be searched
                in cached users, chats or channels, without making any call.

                If a negative integer ID is given, the entity will be searched
                exactly as either a chat (prefixed with ``-``) or as a channel
                (prefixed with ``-100``).

                If a :tl:`Peer` is given, it will be searched exactly in the
                cache as either a user, chat or channel.

                If the given object can be turned into an input entity directly,
                said operation will be done.

                Unsupported types will raise ``TypeError``.

                If the entity can't be found, ``ValueError`` will be raised.

        Returns
            :tl:`InputPeerUser`, :tl:`InputPeerChat` or :tl:`InputPeerChannel`
            or :tl:`InputPeerSelf` if the parameter is ``'me'`` or ``'self'``.

            If you need to get the ID of yourself, you should use
            `get_me` with ``input_peer=True``) instead.

        Example
            .. code-block:: python

                # If you're going to use "username" often in your code
                # (make a lot of calls), consider getting its input entity
                # once, and then using the "user" everywhere instead.
                user = await client.get_input_entity('username')

                # The same applies to IDs, chats or channels.
                chat = await client.get_input_entity(-123456789)
        VFr`   rW   r   Nr   )rU   zCould not find the input entity for {} ({}). Please read https://docs.telethon.dev/en/stable/concepts/entities.html to find out more details.)(r   rV   r   r0   intSUBCLASS_OF_IDrN   rP   rb   rQ   AttributeErrorr   rp   r<   rf   r@   re   ro   Zget_peerPeerUserr   rR   rS   Z	InputUserZuser_idZ	UserEmptyZPeerChatZInputPeerChatr^   ZPeerChannelrk   rl   ZInputChannelZ
channel_idrj   r   ZChannelInvalidErrorrA   typer   )r   rx   rR   rk   r   r   r   rf   b  sT    A 

 zUserMethods.get_input_entity)r   rx   c                    s"   t | |I d H \}}||S r'   )r   Z
resolve_idrb   )r   rx   rC   clsr   r   r   	_get_peer  s    zUserMethods._get_peerT)r   rx   ra   rM   c                    s   t |trtj||dS z|jdkr4| |I dH }W n$ tk
rZ   | |I dH }Y nX t |tjrz| j	ddI dH }tj||dS )a  
        Gets the ID for the given entity.

        This method needs to be ``async`` because `peer` supports usernames,
        invite-links, phone numbers (from people in your contact list), etc.

        If ``add_mark is False``, then a positive ID will be returned
        instead. By default, bot-API style IDs (signed) are returned.

        Example
            .. code-block:: python

                print(await client.get_peer_id('me'))
        r`   )ry      9 NTrZ   )
r0   r{   r   rb   r|   rf   r}   r   rp   rX   )r   rx   ra   r   r   r   rb     s    

zUserMethods.get_peer_idc           	   
      s  t |}|rhz6| tjdI dH jD ]}|j|kr(|  W S q(W n tjk
rb   t	dY nX nf|
 dkr|  I dH S t |\}}|r| tj|I dH }t|tjrt	dnt|tjr|jS n|rz| tj|I dH }W n6 tjk
r* } zt	d||W 5 d}~X Y nX zXt j|jdd t|jtjrht fd	d
|jD W S t fdd
|jD W S W n tk
r   Y nX z| | j|I dH W S  t	k
r   Y nX t	d|dS )a  
        Gets a full entity from the given string, which may be a phone or
        a username, and processes all the found entities on the session.
        The string may also be a user link, or a channel/chat invite link.

        This method has the side effect of adding the found users to the
        session database, so it can be queried later without API calls,
        if this option is enabled on the session.

        Returns the found entity, or raises TypeError if not found.
        r   NzOCannot get entity by phone number as a bot (try using integer IDs, not strings)rz   z^Cannot get entity from a channel (or group) that you are not part of. Join the group and retryzNo user has "{}" as usernameFr`   c                 3   s   | ]}|j  kr|V  qd S r'   rT   r(   pidr   r   r+   :  s     
 z6UserMethods._get_entity_from_string.<locals>.<genexpr>c                 3   s   | ]}|j  kr|V  qd S r'   r   r(   r   r   r   r+   <  s     
 z,Cannot find any entity corresponding to "{}") r   Zparse_phoner   ZcontactsZGetContactsRequestrR   phoner   ZBotMethodInvalidErrorr@   lowerrX   Zparse_usernameri   ZCheckChatInviteRequestr0   r   Z
ChatInviteZChatInviteAlreadyZchatZResolveUsernameRequestZUsernameNotOccupiedErrorrA   rb   rx   r~   rq   rj   StopIterationrw   r<   rf   )	r   stringr   userusernameZis_join_chatZinviterJ   rK   r   r   r   ro   
  sd    






z#UserMethods._get_entity_from_stringc                    sj   z>|j dkr&| |jI dH |_|W S |j dkr<t|W S W n tk
rR   Y nX t| |I dH S )z
        Returns a :tl:`InputDialogPeer`. This is a bit tricky because
        it may or not need access to the client to convert what's given
        into an input entity.
        l   9D Nr   )r|   rf   rx   r   ZInputDialogPeerr}   )r   dialogr   r   r   _get_input_dialogJ  s    

zUserMethods._get_input_dialogc                    s`   z4|j dkr2t|tjr,| |jI dH |_|W S W n tk
rH   Y nX t| |I dH S )z
        Returns a :tl:`InputNotifyPeer`. This is a bit tricky because
        it may or not need access to the client to convert what's given
        into an input entity.
        iXN)r|   r0   r   ZInputNotifyPeerrf   rx   r}   )r   notifyr   r   r   _get_input_notify[  s    

zUserMethods._get_input_notify)FN)FN)F)T)r   
__module____qualname__r$   r"   boolrX   propertytypingOptionalr{   rY   r[   r?   UnionListrw   rf   r   rb   ro   r   r   r   r   r   r   r      s8   p$	 ~   %@r   )r8   datetimerm   r4   r   r   r   r   r   r   r   r   r	   tlr
   r   r   r1   TYPE_CHECKINGZtelegramclientr   	timedeltar   r   r   r   r   r   <module>   s   
