U
    hg                     @   s  d 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	 ddl
mZ
mZ ddlmZ dZd	Zd
ZdZdZG dd dZedZedZejej d Ze Zdd Zdd ZG dd deZG dd deZ G dd dZ!G dd dZ"G dd dZ#G dd  d Z$dS )!a  
This module deals with correct handling of updates, including gaps, and knowing when the code
should "get difference" (the set of updates that the client should know by now minus the set
of updates that it actually knows).

Each chat has its own [`Entry`] in the [`MessageBox`] (this `struct` is the "entry point").
At any given time, the message box may be either getting difference for them (entry is in
[`MessageBox::getting_diff_for`]) or not. If not getting difference, a possible gap may be
found for the updates (entry is in [`MessageBox::possible_gaps`]). Otherwise, the entry is
on its happy path.

Gaps are cleared when they are either resolved on their own (by waiting for a short time)
or because we got the difference for the corresponding entry.

While there are entries for which their difference must be fetched,
[`MessageBox::check_deadlines`] will always return [`Instant::now`], since "now" is the time
to get the difference.
    N)Enum   )SessionStateChannelState   )types	functions)get_running_loopi d   g      ?i  c                   @   s"   e Zd ZdZdddZdd ZdS )SentineltagNc                 C   s   |pd| _ d S )N_r   )selfr    r   @/tmp/pip-unpacked-wheel-c81u5j2r/telethon/_updates/messagebox.py__init__4   s    zSentinel.__init__c                 C   s   | j S Nr   r   r   r   r   __repr__7   s    zSentinel.__repr__)N)__name__
__module____qualname__	__slots__r   r   r   r   r   r   r   1   s   
r   ZACCOUNTZSECRETc                   C   s   t   t S r   )r	   timeNO_UPDATES_TIMEOUTr   r   r   r   next_updates_deadlineG   s    r   c                   C   s$   t j tdd d  jt jjdS )Nr      )tzinfo)datetimer   gmtimereplacetimezoneutcr   r   r   r   epochJ   s    r$   c                   @   s   e Zd Zdd ZdS )GapErrorc                 C   s   dS )Nz
GapError()r   r   r   r   r   r   N   s    zGapError.__repr__N)r   r   r   r   r   r   r   r   r%   M   s   r%   c                   @   s   e Zd ZdZdZdS )PrematureEndReasontmpZbanN)r   r   r   TEMPORARY_SERVER_ISSUESBANNEDr   r   r   r   r&   R   s   r&   c                   @   s6   e Zd ZdZeeedddZedd Zdd Z	dS )	PtsInfopts	pts_countentryc                 C   s   || _ || _|| _d S r   r+   )r   r,   r-   r.   r   r   r   r   [   s    zPtsInfo.__init__c                 C   s   t |dd }|rbt |dd pd}z|jjj}W n$ tk
rR   t |dd pLt}Y nX | |||dS t |dd }|r| |dtdS d S )Nr,   r-   r   
channel_idr+   qtsr   )getattrmessageZpeer_idr/   AttributeErrorENTRY_ACCOUNTENTRY_SECRET)clsupdater,   r-   r.   r0   r   r   r   from_updatee   s    zPtsInfo.from_updatec                 C   s   d| j  d| j d| j dS )NzPtsInfo(pts=z, pts_count=z, entry=)r+   r   r   r   r   r   v   s    zPtsInfo.__repr__N)
r   r   r   r   intobjectr   classmethodr8   r   r   r   r   r   r*   X   s   

r*   c                   @   s(   e Zd ZdZeedddZdd ZdS )Stater,   deadlinec                 C   s   || _ || _d S r   r>   )r   r,   r?   r   r   r   r   ~   s    zState.__init__c                 C   s   d| j  d| j dS )Nz
State(pts=z, deadline=r9   r>   r   r   r   r   r      s    zState.__repr__N)r   r   r   r   r:   floatr   r   r   r   r   r   r=   {   s
   
r=   c                   @   s(   e Zd ZdZeedddZdd ZdS )PossibleGapr?   updatesc                 C   s   || _ || _d S r   rB   )r   r?   rC   r   r   r   r      s    zPossibleGap.__init__c                 C   s   d| j  dt| j dS )NzPossibleGap(deadline=z, update_count=r9   )r?   lenrC   r   r   r   r   r      s    zPossibleGap.__repr__N)r   r   r   r   r@   listr   r   r   r   r   r   rA      s
   	rA   c                   @   s   e Zd ZdZee ejdd edeefe	eje
ee	edddZdd	 Zd
d Zdd ZedddZdd Zdd Zdd Zd4ddZdd Zdd Zdd Zd d! Zdgd"d#d$Zd%d& Zd'd( Zd)d* Zd+d, Zd-d. Z d/d0 Z!e"d1d2d3Z#dS )5
MessageBox)_logmapdateseqnext_deadlinepossible_gapsgetting_diff_forr   )secondsN)rH   rI   rJ   rK   rL   rM   c                 C   s^   || _ |tkri n|| _|| _|| _|| _|tkr6i n|| _|tkrJt n|| _| 	d d S )NzMessageBox initialized)
rG   	_sentinelrH   rI   rJ   rK   rL   setrM   _trace)r   logrH   rI   rJ   rK   rL   rM   r   r   r   r      s    zMessageBox.__init__c                 O   s8   | j td| j| j | j | j jt|f|| d S )Nz7Current MessageBox state: seq = %r, date = %s, map = %r)rG   rR   LOG_LEVEL_TRACErJ   rI   	isoformatrH   )r   msgargskwargsr   r   r   rQ      s    
  zMessageBox._tracec                    s   |  d|| t  | j  |jtkr<t|j d| jt< |jtkrZt|j d| jt	< | j
 fdd|D  tjj|jtjjd| _|j| _t| _dS )zO
        Create a [`MessageBox`] from a previously known update state.
        z?Loading MessageBox with session_state = %r, channel_states = %rr>   c                 3   s"   | ]}|j t|j d fV  qdS )r>   N)r/   r=   r,   ).0sr?   r   r   	<genexpr>   s     z"MessageBox.load.<locals>.<genexpr>)tzN)rQ   r   rH   clearr,   NO_SEQr=   r4   r0   r5   r7   r   fromtimestamprI   r"   r#   rJ   rK   )r   session_stateZchannel_statesr   rZ   r   load   s    


zMessageBox.loadc                 C   sT   t t| jkr| jt jntt| jkr0| jt jnt| j| jddd | j D fS )zb
        Return the current state.

        This should be used for persisting the state.
        )r,   r0   rI   rJ   c                 S   s"   i | ]\}}t |tr||jqS r   )
isinstancer:   r,   )rX   idstater   r   r   
<dictcomp>   s     
  z,MessageBox.session_state.<locals>.<dictcomp>)	dictr4   rH   r,   r^   r5   rI   rJ   itemsr   r   r   r   r`      s    zMessageBox.session_state)returnc                 C   s
   t | jkS )zO
        Return true if the message box is empty and has no state yet.
        )r4   rH   r   r   r   r   is_empty   s    zMessageBox.is_emptyc                    s   t    | jr S t }| jr@t|fdd | j D  }n | j| jkr`t|| j| j j	} |kr| j
 fdd| j D  | j
 fdd| j D  | d| j | jD ]}| j|d q|S )a*  
        Return the next deadline when receiving updates should timeout.

        If a deadline expired, the corresponding entries will be marked as needing to get its difference.
        While there are entries pending of getting their difference, this method returns the current instant.
        c                 s   s   | ]}|j V  qd S r   rZ   )rX   gapr   r   r   r[     s     z-MessageBox.check_deadlines.<locals>.<genexpr>c                 3   s    | ]\}} |j kr|V  qd S r   rZ   rX   r.   rj   nowr   r   r[     s     
 c                 3   s    | ]\}} |j kr|V  qd S r   rZ   )rX   r.   rd   rl   r   r   r[     s     
 z&Deadlines met, now getting diff for %rN)r	   r   rM   r   rL   minvaluesrK   rH   r?   r7   rg   rQ   pop)r   r?   r.   r   rl   r   check_deadlines   s    
   
zMessageBox.check_deadlinesc                 C   s   |sd S |D ]"}|| j kr"td|| j | _q| j|krXt| j  dd dd | _n$| j| j kr||| j | j jk r||| _d S )Nz@Called reset_deadline on an entry for which we do not have statec                 S   s
   | d j S )Nr   rZ   )Zentry_stater   r   r   <lambda>3      z,MessageBox.reset_deadlines.<locals>.<lambda>keyr   )rH   RuntimeErrorr?   rK   rn   rg   )r   entriesr?   r.   r   r   r   reset_deadlines)  s    

zMessageBox.reset_deadlinesc                 C   s    |  |ht  |pt  d S r   )rx   r	   r   r   )r   r/   timeoutr   r   r   reset_channel_deadline;  s    z!MessageBox.reset_channel_deadlineTc                 C   s   |  d| t }|jtks |s6t|j|d| jt< n| jtd  |jtksR|sht|j|d| jt	< n| jt	d  |j
| _
|j| _d S )NzSetting state %sr>   )rQ   r   r,   r^   r=   rH   r4   rp   r0   r5   rI   rJ   )r   rd   resetr?   r   r   r   	set_stateB  s    	zMessageBox.set_statec                 C   s0   |  d|| || jkr,t|t d| j|< d S )Nz&Trying to set channel state for %r: %rr>   )rQ   rH   r=   r   )r   rc   r,   r   r   r   try_set_channel_state_  s    
z MessageBox.try_set_channel_statec                 C   sZ   || j kr.|| jkrtd| d|| d S | d|| | j| | j|d  d S )Nz@Should not have a possible_gap for an entry not in the state mapzFShould get difference for %r because %s but cannot due to missing hashz+Marking %r as needing difference because %s)rH   rL   rv   rQ   rM   addrp   )r   r.   reasonr   r   r   try_begin_get_diffj  s    

zMessageBox.try_begin_get_diffc                 C   sT   z| j | W n tk
r,   tdY nX | |ht  || jksPtdd S )Nz>Called end_get_diff on an entry which was not getting diff forz2gaps shouldn't be created while getting difference)rM   removeKeyErrorrv   rx   r   rL   AssertionError)r   r.   r   r   r   end_get_diff|  s    zMessageBox.end_get_diffc              	      s~  t |dd}|}g }t |dd }t |dd }t |dd }t |dd pFg }	t |dd pVg }
d|||rn| nd | |d krtd	 t|d krt}|d kr|}t |d
d pt|tj	r|j
n|g}|D ]
}||_q|tkr&jd |krd |	|
fS jd |k r&td tdd }t dg |td  fddt||dD   d rd |t kr|_|tkr|_t  jrbdtj tj D ]z}j| jj|d ttj| jD ]J}j| jd}j|d d}|r|| dt || qqdd j! D _|dd |D  |	|
fS )N_self_outgoingFrI   rJ   	seq_startuserschatsz?Processing updates with seq = %r, seq_start = %r, date = %s: %szreceived updatesTooLongrC   r   z9Skipping updates as they should have already been handledzdetected gapc                 S   s   t | }|r|j|j S dS )Nr   )r*   r8   r,   r-   )r7   r,   r   r   r   
_sort_gaps  s    
z.MessageBox.process_updates.<locals>._sort_gapsc                 3   s   | ]}j | d V  qdS ))rx   any_pts_appliedN)apply_pts_inforX   ur   rx   r   r   r   r[     s   z-MessageBox.process_updates.<locals>.<genexpr>rt   r   z)Updating seq as local pts was updated tooz#Trying to re-apply %r possible gaps)rx   zResolved gap with %r: %sc                 S   s   i | ]\}}|j r||qS r   )rC   rk   r   r   r   re     s       z.MessageBox.process_updates.<locals>.<dictcomp>c                 s   s   | ]}|j s|V  qd S r   )r   r   r   r   r   r[     s      )"r1   rQ   rT   r   r4   r%   r^   rb   tlZUpdateShortr7   r   rJ   rP   extendfiltersortedr$   rI   rx   r   rL   rD   rE   keysrC   sortrangerp   r   appendr*   r8   rg   )r   rC   chat_hashesresultZself_outgoingZreal_resultrI   rJ   r   r   r   r   r   ru   r   r7   r   r   r   process_updates  sr    
   $







zMessageBox.process_updates)r   c                C   sv  t |tjr| |jd d S t|}|s<| d| |S |rL||j	 |j	| j
krh| d| d S |j	| jkr$| j|j	 j}||j |jkr| d||| d S ||j |jk r| d||| |j	| jkrtt  t g d| j|j	< | j|j	 j| d S d|d< | d	||| |j	| jkrD|j| j|j	 _n.t|j|jrVdnd
 p`d
t d| j|j	< |S )Nzreceived updateChannelTooLongz7No pts in update, so it can be applied in any order: %sz:Skipping update with %r as its difference is being fetchedz+Skipping update since local pts %r > %r: %sz(Possible gap since local pts %r < %r: %srB   Tr   z/Applying update pts since local pts %r = %r: %sr   r>   )rb   r   ZUpdateChannelTooLongr   r/   r*   r8   rQ   r~   r.   rM   rH   r,   r-   rL   rA   r	   r   POSSIBLE_GAP_TIMEOUTrC   r   r=   r   )r   r7   rx   r   r,   Z	local_ptsr   r   r   r     sF    
zMessageBox.apply_pts_infoc                 C   sv   t tfD ]h}|| jkr|| jkr(tdtjj| jt  jd | j	t| jkrT| jt jnt
d}| d| |  S qd S )NAShould not try to get difference for an entry without known state)r,   Zpts_total_limitrI   r0   z Requesting account difference %s)r4   r5   rM   rH   rv   fnrC   ZGetDifferenceRequestr,   rI   r^   rQ   )r   r.   gdr   r   r   get_differenceh  s    



zMessageBox.get_differencec                 C   s  |  d| d }d }t|tjjrBd}|j| _|j| _g g g f}nt|tjjrrd}||j	|j
 | ||}nZt|tjjrd}||j	|j
 | ||}n*t|tjjrd}|j| jt _g g g f}|rt| jk}t| jk}|s|std|r| t |r| t |S )NzApplying account difference %sTFzXShould not be applying the difference when neither account or secret was diff was active)rQ   rb   r   rC   ZDifferenceEmptyrI   rJ   Z
Differencer   r   r   apply_difference_typeZDifferenceSliceZDifferenceTooLongr,   rH   r4   rM   r5   rv   r   )r   diffr   finishr   accountsecretr   r   r   apply_difference{  s<    




zMessageBox.apply_differencec              	   C   s   t |dd p|j}| j|dd g }| tj|j|j|jt	 t
d|| |dd |jD  |dd |jD  ||j|jfS )NZintermediate_stateF)r{   rC   r   r   rI   rJ   c                 s   s   | ]}t j|ttd V  qdS )r2   r,   r-   N)r   ZUpdateNewMessager^   rX   mr   r   r   r[     s   z3MessageBox.apply_difference_type.<locals>.<genexpr>c                 s   s   | ]}t j|td V  qdS ))r2   r0   N)r   ZUpdateNewEncryptedMessager^   r   r   r   r   r[     s
   )r1   rd   r|   r   r   Updatesother_updatesr   r   r$   r^   r   new_messagesZnew_encrypted_messages)r   r   r   rd   rC   r   r   r   r     s(     

z MessageBox.apply_difference_typec                 C   sN   |  d t| jk}t| jk}|s.|s.td|r<| t |rJ| t d S )NzEnding account differencezVShould not be ending get difference when neither account or secret was diff was active)rQ   r4   rM   r5   rv   r   )r   r   r   r   r   r   end_difference  s    



zMessageBox.end_differencec                 C   s   t dd | jD d }|sd S ||}|sH| | | j|d  d S | j|}|s`tdtjj	dt
|j|jt
 |j|jrtntd}| d| |S )Nc                 s   s   | ]}t |tr|V  qd S r   )rb   r:   )rX   rc   r   r   r   r[     s     
 z4MessageBox.get_channel_difference.<locals>.<genexpr>r   F)forcechannelr   r,   limitz Requesting channel difference %s)nextrM   getr   rH   rp   rv   r   rC   ZGetChannelDifferenceRequestr   ZInputChannelrc   hashZChannelMessagesFilterEmptyr,   Zself_botBOT_CHANNEL_DIFF_LIMITUSER_CHANNEL_DIFF_LIMITrQ   )r   r   r.   packedrd   r   r   r   r   get_channel_difference  s(    

z!MessageBox.get_channel_differencec              	   C   sJ  |j j}| d|| | j|d  t|tjjr^|j	s<t
| | |j| j| _g g g fS t|tjjr|j	svt
|jj| j| _||j|j | ||j g g g fS t|tjjrF|j	r| | |j| j| _||j|j g }| tj|j|j|jt td|| |dd |jD  | |d  ||j|jfS d S )Nz&Applying channel difference for %r: %sr   c                 s   s   | ]}t j|ttd V  qdS r   )r   ZUpdateNewChannelMessager^   r   r   r   r   r[   '  s   z6MessageBox.apply_channel_difference.<locals>.<genexpr>)r   r/   rQ   rL   rp   rb   r   rC   ZChannelDifferenceEmptyfinalr   r   r,   rH   ZChannelDifferenceTooLongdialogr   r   r   rz   ry   ZChannelDifferencer   r   r   r$   r^   r   )r   requestr   r   r.   rC   r   r   r   apply_channel_difference  sF    





 
z#MessageBox.apply_channel_difference)r   c                 C   sr   |j j}| d|| |tjkr:| j|d  | | n4|tjkrf| j|d  | | | j	|= nt
dd S )Nz+Ending channel difference for %r because %sz(Unknown reason to end channel difference)r   r/   rQ   r&   r(   rL   rp   r   r)   rH   rv   )r   r   r   r   r.   r   r   r   end_channel_difference0  s    



z!MessageBox.end_channel_difference)T)$r   r   r   r   rO   r$   r   	timedeltar^   rf   r:   r;   rP   r   rQ   ra   r`   boolri   rq   rx   rz   r|   r}   r   r   r   r   r   r   r   r   r   r   r&   r   r   r   r   r   rF      sH   		$)
x`,"3rF   )%__doc__Zasyncior   r   loggingenumr   sessionr   r   r   r   r   r   Zhelpersr	   r^   r   r   r   r   r   r4   r5   DEBUGNOTSETrS   rO   r   r$   
ValueErrorr%   r&   r*   r=   rA   rF   r   r   r   r   <module>   s4   	#