o
    XzjËK  ã                   @   sh   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 ddlmZm	Z	m
Z
 dZdd„ ZG d	d
„ d
eƒZdS )é    Né   )Ú
ChatGetteré   )ÚhelpersÚutilsÚerrorsgü©ñÒMbP?c                    s   t  ˆ ¡‡ fdd„ƒ}|S )Nc                    s&   | j rt d¡‚ˆ | g|¢R i |¤ŽS )Nz%The conversation was cancelled before)Ú
_cancelledÚasyncioÚCancelledError)ÚselfÚargsÚkwargs©Úf© úZ/var/www/kodo/tsheker/venv/lib/python3.10/site-packages/telethon/tl/custom/conversation.pyÚwrapper   s   
z"_checks_cancelled.<locals>.wrapper)Ú	functoolsÚwraps)r   r   r   r   r   Ú_checks_cancelled   s   r   c                   @   s  e Zd ZdZdZdZdd„ Zedd„ ƒZedd„ ƒZ	ed/d
d„ƒZ
d/d	dœdd„Zd/d	dœdd„Zdd„ Zd/d	dœdd„Zd/d	dœdd„Zd	dœdd„Zdd„ Zdd„ Zdd„ Zdd „ Zd!d"„ Zed#d$„ ƒZd/d%d&„Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ ZejZejZ d	S )0ÚConversationaT  
    Represents a conversation inside an specific chat.

    A conversation keeps track of new messages since it was
    created until its exit and easily lets you query the
    current state.

    If you need a conversation across two or more chats,
    you should use two conversations and synchronize them
    as you better see fit.
    r   c                C   s°   t j| |d� tj| _t jd7  _|| _|| _|| _d | _t	ƒ | _
d| _g | _d| _|| _d | _i | _i | _i | _i | _i | _|| _d| _i | _|rP| j| _ni | _i | _d S )N)Ú
input_chatr   r   F)r   Ú__init__r   Ú_id_counterÚ_idÚ_clientÚ_timeoutÚ_total_timeoutÚ
_total_dueÚsetÚ	_outgoingÚ_last_outgoingÚ	_incomingÚ_last_incomingÚ_max_incomingÚ
_last_readÚ_customÚ_pending_responsesÚ_pending_repliesÚ_pending_editsÚ_pending_readsÚ
_exclusiver   Ú_response_indicesÚ_reply_indicesÚ_edit_dates)r   Úclientr   ÚtimeoutÚtotal_timeoutÚmax_messagesÚ	exclusiveÚreplies_are_responsesr   r   r   r   )   s2   

zConversation.__init__c                 Ï   ó^   �| j j| jg|¢R i |¤ŽI dH }t|tƒr|n|f}| j dd„ |D ƒ¡ |d j| _|S )z¸
        Sends a message in the context of this conversation. Shorthand
        for `telethon.client.messages.MessageMethods.send_message` with
        ``entity`` already set.
        Nc                 s   ó   � | ]}|j V  qd S ©N©Úid©Ú.0Úmr   r   r   Ú	<genexpr>]   ó   € z,Conversation.send_message.<locals>.<genexpr>éÿÿÿÿ)	r   Úsend_messageÚ_input_chatÚ
isinstanceÚlistr    Úupdater9   r!   ©r   r   r   ÚsentÚmsr   r   r   r@   Q   ó   €ÿÿÿzConversation.send_messagec                 Ï   r5   )z°
        Sends a file in the context of this conversation. Shorthand
        for `telethon.client.uploads.UploadMethods.send_file` with
        ``entity`` already set.
        Nc                 s   r6   r7   r8   r:   r   r   r   r=   m   r>   z)Conversation.send_file.<locals>.<genexpr>r?   )	r   Ú	send_filerA   rB   rC   r    rD   r9   r!   rE   r   r   r   rI   a   rH   zConversation.send_fileNc                 C   sD   |du r| j r| j d j}nd}nt|tƒs|j}| jj| j|d�S )a,  
        Marks as read the latest received message if ``message is None``.
        Otherwise, marks as read until the given message (or message ID).

        This is equivalent to calling `client.send_read_acknowledge
        <telethon.client.messages.MessageMethods.send_read_acknowledge>`.
        Nr?   r   )Úmax_id)r"   r9   rB   Úintr   Úsend_read_acknowledgerA   ©r   Úmessager   r   r   Ú	mark_readq   s   	
ÿzConversation.mark_read©r0   c                C   ó   |   || j| j|dd„ ¡S )aŒ  
        Gets the next message that responds to a previous one. This is
        the method you need most of the time, along with `get_edit`.

        Args:
            message (`Message <telethon.tl.custom.message.Message>` | `int`, optional):
                The message (or the message ID) for which a response
                is expected. By default this is the last sent message.

            timeout (`int` | `float`, optional):
                If present, this `timeout` (in seconds) will override the
                per-action timeout defined for the conversation.

        .. code-block:: python

            async with client.conversation(...) as conv:
                await conv.send_message('Hey, what is your name?')

                response = await conv.get_response()
                name = response.text

                await conv.send_message('Nice to meet you, {}!'.format(name))
        c                 S   s   dS ©NTr   ©ÚxÚyr   r   r   Ú<lambda>Ÿ   s    z+Conversation.get_response.<locals>.<lambda>)Ú_get_messager,   r'   ©r   rN   r0   r   r   r   Úget_response…   s   þzConversation.get_responsec                C   rQ   )zR
        Gets the next message that explicitly replies to a previous one.
        c                 S   s   | j o| j j|kS r7   )Úreply_toÚreply_to_msg_idrS   r   r   r   rV   ¨   s    z(Conversation.get_reply.<locals>.<lambda>)rW   r-   r(   rX   r   r   r   Ú	get_reply¢   s   þzConversation.get_replyc                 C   sÀ   t   ¡ }|  |¡}||vr)t| jƒD ]\}}	|	j|kr!|||<  nqt| jƒ||< | jj ¡ }
|| }|t| jƒk rS| j| }	||	|ƒrS||  d7  < |
 	|	¡ |
S |
||< |  
|
||||¡S )a  
        Gets the next desired message under the desired condition.

        Args:
            target_message (`object`):
                The target message for which we want to find another
                response that applies based on `condition`.

            indices (`dict`):
                This dictionary remembers the last ID chosen for the
                input `target_message`.

            pending (`dict`):
                This dictionary remembers {msg_id: Future} to be set
                once `condition` is met.

            timeout (`int`):
                The timeout (in seconds) override to use for this operation.

            condition (`callable`):
                The condition callable that checks if an incoming
                message is a valid response.
        r   )ÚtimeÚ_get_message_idÚ	enumerater"   r9   Úlenr   ÚloopÚcreate_futureÚ
set_resultÚ_get_result)r   Útarget_messageÚindicesÚpendingr0   Ú	conditionÚ
start_timeÚ	target_idÚiÚincomingÚfutureÚlast_idxr   r   r   rW   «   s&   

þ


zConversation._get_messagec                   sž   t   ¡ }|  |¡‰| j ˆd¡‰ t‡ ‡fdd„| jD ƒdd„ dd�}| jj ¡ }|r@|j	 
¡ ˆ kr@|j	 
¡ | jˆ< | |¡ |S || jˆ< |  |||| jˆ¡S )z†
        Awaits for an edit after the last message to arrive.
        The arguments are the same as those for `get_response`.
        r   c                 3   s2   � | ]}|j r|jˆkr|j  ¡ ˆ kr|V  qd S r7   )Ú	edit_dater9   Ú	timestamp)r;   rT   ©Útarget_daterj   r   r   r=   ò   s   € ÿ
ýýz(Conversation.get_edit.<locals>.<genexpr>c                 S   s
   | j  ¡ S r7   )ro   rp   )rT   r   r   r   rV   ÷   s   
 z'Conversation.get_edit.<locals>.<lambda>N)ÚkeyÚdefault)r]   r^   r.   ÚgetÚminr"   r   ra   rb   ro   rp   rc   r)   rd   )r   rN   r0   ri   Úearliest_editrm   r   rq   r   Úget_editè   s   
ù


zConversation.get_editc                C   s^   t   ¡ }| jj ¡ }|  |¡}| jdu r|d | _| j|kr dS || j|< |  |||| j|¡S )zÑ
        Awaits for the sent message to be marked as read. Note that
        receiving a response doesn't imply the message was read, and
        this action will also trigger even without a response.
        Nr   )r]   r   ra   rb   r^   r%   r*   rd   )r   rN   r0   ri   rm   rj   r   r   r   Ú	wait_read  s   




zConversation.wait_readc             
   Ã   s–   �t   ¡ }t|tƒr|ƒ }| | j¡I dH  tj}t jd7  _| jj ¡ }||f| j	|< z|  
|||| j	|¡I dH W | j	 |d¡ S | j	 |d¡ w )a$  
        Waits for a custom event to occur. Timeouts still apply.

        .. note::

            **Only use this if there isn't another method available!**
            For example, don't use `wait_event` for new messages,
            since `get_response` already exists, etc.

        Unless you're certain that your code will run fast enough,
        generally you should get a "handle" of this special coroutine
        before acting. In this example you will see how to wait for a user
        to join a group with proper use of `wait_event`:

        .. code-block:: python

            from telethon import TelegramClient, events

            client = TelegramClient(...)
            group_id = ...

            async def main():
                # Could also get the user id from an event; this is just an example
                user_id = ...

                async with client.conversation(user_id) as conv:
                    # Get a handle to the future event we'll wait for
                    handle = conv.wait_event(events.ChatAction(
                        group_id,
                        func=lambda e: e.user_joined and e.user_id == user_id
                    ))

                    # Perform whatever action in between
                    await conv.send_message('Please join this group before speaking to me!')

                    # Wait for the event we registered above to fire
                    event = await handle

                    # Continue with the conversation
                    await conv.send_message('Thanks!')

        This way your event can be registered before acting,
        since the response may arrive before your event was
        registered. It depends on your use case since this
        also means the event can arrive before you send
        a previous action.
        Nr   )r]   rB   ÚtypeÚresolver   r   Ú_custom_counterra   rb   r&   rd   Úpop)r   Úeventr0   ri   Úcounterrm   r   r   r   Ú
wait_event  s   €0
 zConversation.wait_eventc                 Ã   sj   �t | j ¡ ƒD ]*\}\}}t|ƒ}|| }|r2| |¡}t |¡r'|I d H }|r2| |¡ | j|= qd S r7   )rC   r&   Úitemsrz   ÚfilterÚinspectÚisawaitablerc   )r   Úbuiltrs   ÚevÚfutÚev_typeÚinstr‚   r   r   r   Ú_check_customZ  s   €



€õzConversation._check_customc                 C   sÔ   |j }|j| jks|jrd S t| jƒ| jkr|  tdƒ¡ d S | j |¡ t	| j
 ¡ ƒD ]\}}t| jƒ| j|< | |¡ | j
|= q,t	| j ¡ ƒD ]\}}|jrg||jjkrgt| jƒ| j|< | |¡ | j|= qId S )NzToo many incoming messages)rN   Úchat_idÚoutr`   r"   r$   Ú_cancel_allÚ
ValueErrorÚappendrC   r'   r�   r,   rc   r(   rZ   r[   r-   )r   ÚresponseÚmsg_idrm   r   r   r   Ú_on_new_messageh  s$   


€üzConversation._on_new_messagec                 C   sÂ   |j }|j| jks|jrd S t| jƒD ]\}}|j|jkr$|| j|<  nqt| j ¡ ƒD ]2\}}||jk r^|j	 
¡ }|| j |d¡krM| j|  t7  < n|j	 
¡ | j|< | |¡ | j|= q,d S )Nr   )rN   r‹   rŒ   r_   r"   r9   rC   r)   r�   ro   rp   r.   ru   Ú_EDIT_COLLISION_DELTArc   )r   rN   rk   r<   r‘   rm   Úedit_tsr   r   r   Ú_on_editƒ  s$   
þ


€ózConversation._on_editc                 C   sV   |j | j ks	|jrd S |j| _t| j ¡ ƒD ]\}}|| jkr(| d¡ | j|= qd S rR   )r‹   ÚinboxrJ   r%   rC   r*   r�   rc   )r   r~   r‘   rg   r   r   r   Ú_on_read�  s   

€ýzConversation._on_readc                 C   s0   |d urt |tƒr|S |jS | jr| jS tdƒ‚)NzNo message was sent previously)rB   rK   r9   r!   rŽ   rM   r   r   r   r^   ¨  s
   zConversation._get_message_idc                 C   sT   | j }|d u r
| j}|d urt||| ƒ}tj||tdƒkr"d d�S |t ¡  d�S )NÚinfrP   )r   r   rv   r	   Úwait_forÚfloatr]   )r   rm   ri   r0   rg   rj   Úduer   r   r   rd   °  s   þ
þzConversation._get_resultc                 C   st   d| _ t | j ¡ | j ¡ | j ¡ ¡D ]}|r| |¡ q| ¡  q| j	 ¡ D ]\}}|r3| |¡ q'| ¡  q'd S rR   )
r   Ú	itertoolsÚchainr'   Úvaluesr(   r)   Úset_exceptionÚcancelr&   )r   Ú	exceptionrg   Ú_r‡   r   r   r   r�   Ã  s   ý

üzConversation._cancel_allc              	   Ã   sÊ   �| j  | j¡I d H | _t | j¡| _t | j¡}| j j| }| jr(|r(t	 
¡ ‚| | ¡ d| _d| _d| _| j| j| j| j| j| j| j| j| jf	D ]}| ¡  qJ| jr^t ¡ | j | _| S tdƒ| _| S )NFr   r˜   )r   Úget_input_entityrA   r   Úget_peerÚ
_chat_peerÚget_peer_idÚ_conversationsr+   r   ÚAlreadyInConversationErrorÚaddr   r!   r#   r    r"   r'   r(   r)   r,   r-   r.   r&   Úclearr   r]   r   rš   )r   r‹   Úconv_setÚdr   r   r   Ú
__aenter__Ô  s.   €ÿ

ü

þzConversation.__aenter__c                 C   s   |   ¡  dS )zÚ
        Cancels the current conversation. Pending responses and subsequent
        calls to get a response will raise ``asyncio.CancelledError``.

        This method is synchronous and should not be awaited.
        N)r�   )r   r   r   r   r    ó  s   zConversation.cancelc                 Ã   s4   �| j  | j¡I dH }| j j| D ]}| ¡  qdS )zÞ
        Calls `cancel` on *all* conversations in this chat.

        Note that you should ``await`` this method, since it's meant to be
        used outside of a context manager, and it needs to resolve the chat.
        N)r   r¦   rA   r§   r    )r   r‹   Úconvr   r   r   Ú
cancel_allü  s
   €
ÿzConversation.cancel_allc                 Ã   s>   �t  | j¡}| jj| }| | ¡ |s| jj|= |  ¡  d S r7   )r   r¦   r¥   r   r§   Údiscardr�   )r   Úexc_typeÚexc_valÚexc_tbr‹   r«   r   r   r   Ú	__aexit__  s   €

zConversation.__aexit__r7   )!Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r|   r   r   r@   rI   rO   rY   r\   rW   rx   ry   r€   rŠ   r’   r•   r—   r^   rd   r�   r­   r    r¯   r´   r   Ú_sync_enterÚ	__enter__Ú
_sync_exitÚ__exit__r   r   r   r   r      s>    (

	=B

		
r   )r	   r   rƒ   rœ   r]   Ú
chatgetterr   Ú r   r   r   r“   r   r   r   r   r   r   Ú<module>   s    
