U
    g++                     @   s   d Z ddlZddlZddlZddlmZmZmZmZm	Z	m
Z
mZmZ zddlmZ dZW n ek
rp   dZY nX ddlmZ ddlmZ dd	lmZ dd
lmZ ejdkrejZnejed dddZeeddZG dd dee Z dS )zeThis module contains an implementation of the BaseRateLimiter class based on the aiolimiter
library.
    N)AnyAsyncIteratorCallable	CoroutineDictListOptionalUnion)AsyncLimiterTF)
get_logger)JSONDict)
RetryAfter)BaseRateLimiter)   
   returnc                   C  s
   d V  d S N r   r   r   @/tmp/pip-unpacked-wheel-swnnwir2/telegram/ext/_aioratelimiter.pynull_context.   s    r   AIORateLimiter)
class_namec                   @   s  e Zd ZdZdZdeeeeedd	d
dZddddZddddZ	e
eeef ddddZee
eeef edeeee
eeee f f f eeeef e
eeee f dddZedeeee
eeee f f f eeeef eeeef ee e
eeee f dddZdS )r   a  
    Implementation of :class:`~telegram.ext.BaseRateLimiter` using the library
    `aiolimiter <https://aiolimiter.readthedocs.io/en/stable>`_.

    Important:
        If you want to use this class, you must install PTB with the optional requirement
        ``rate-limiter``, i.e.

        .. code-block:: bash

           pip install "python-telegram-bot[rate-limiter]"

    The rate limiting is applied by combining two levels of throttling and :meth:`process_request`
    roughly boils down to::

        async with group_limiter(group_id):
            async with overall_limiter:
                await callback(*args, **kwargs)

    Here, ``group_id`` is determined by checking if there is a ``chat_id`` parameter in the
    :paramref:`~telegram.ext.BaseRateLimiter.process_request.data`.
    The ``overall_limiter`` is applied only if a ``chat_id`` argument is present at all.

    Attention:
        * Some bot methods accept a ``chat_id`` parameter in form of a ``@username`` for
          supergroups and channels. As we can't know which ``@username`` corresponds to which
          integer ``chat_id``, these will be treated as different groups, which may lead to
          exceeding the rate limit.
        * As channels can't be differentiated from supergroups by the ``@username`` or integer
          ``chat_id``, this also applies the group related rate limits to channels.
        * A :exc:`~telegram.error.RetryAfter` exception will halt *all* requests for
          :attr:`~telegram.error.RetryAfter.retry_after` + 0.1 seconds. This may be stricter than
          necessary in some cases, e.g. the bot may hit a rate limit in one group but might still
          be allowed to send messages in another group.

    Note:
        This class is to be understood as minimal effort reference implementation.
        If you would like to handle rate limiting in a more sophisticated, fine-tuned way, we
        welcome you to implement your own subclass of :class:`~telegram.ext.BaseRateLimiter`.
        Feel free to check out the source code of this class for inspiration.

    .. seealso:: :wiki:`Avoiding Flood Limits <Avoiding-flood-limits>`

    .. versionadded:: 20.0

    Args:
        overall_max_rate (:obj:`float`): The maximum number of requests allowed for the entire bot
            per :paramref:`overall_time_period`. When set to 0, no rate limiting will be applied.
            Defaults to ``30``.
        overall_time_period (:obj:`float`): The time period (in seconds) during which the
            :paramref:`overall_max_rate` is enforced.  When set to 0, no rate limiting will be
            applied. Defaults to 1.
        group_max_rate (:obj:`float`): The maximum number of requests allowed for requests related
            to groups and channels per :paramref:`group_time_period`.  When set to 0, no rate
            limiting will be applied. Defaults to 20.
        group_time_period (:obj:`float`): The time period (in seconds) during which the
            :paramref:`group_max_rate` is enforced.  When set to 0, no rate limiting will be
            applied. Defaults to 60.
        max_retries (:obj:`int`): The maximum number of retries to be made in case of a
            :exc:`~telegram.error.RetryAfter` exception.
            If set to 0, no retries will be made. Defaults to ``0``.

    )_base_limiter_group_limiters_group_max_rate_group_time_period_max_retries_retry_after_event         <   r   N)overall_max_rateoverall_time_periodgroup_max_rategroup_time_periodmax_retriesr   c                 C   sp   t std|r$|r$t||d| _nd | _|r@|r@|| _|| _nd| _d| _i | _|| _t	 | _
| j
  d S )NzeTo use `AIORateLimiter`, PTB must be installed via `pip install "python-telegram-bot[rate-limiter]"`.max_rateZtime_periodr   )AIO_LIMITER_AVAILABLERuntimeErrorr
   r   r   r   r   r   asyncioEventr   set)selfr#   r$   r%   r&   r'   r   r   r   __init__   s&     

zAIORateLimiter.__init__r   c                    s   dS zDoes nothing.Nr   r/   r   r   r   
initialize   s    zAIORateLimiter.initializec                    s   dS r1   r   r2   r   r   r   shutdown   s    zAIORateLimiter.shutdownr
   )group_idr   c                 C   sn   t | jdkrD| j  D ]&\}}||kr.q||jr| j|= q|| jkrdt| j| jd| j|< | j| S )Ni   r(   )	lenr   copyitemsZhas_capacityr)   r
   r   r   )r/   r5   keyZlimiterr   r   r   _get_group_limiter   s    

z!AIORateLimiter._get_group_limiter.)chatgroupcallbackargskwargsr   c                    s   |r| j r| j nt }|r*| jr*| |nt }|4 I d H d |4 I d H F | j I d H  |||I d H W  5 Q I d H R  W  5 Q I d H R  S Q I d H R X W 5 Q I d H R X d S r   )r   r   r   r:   r   wait)r/   r;   r<   r=   r>   r?   Zbase_contextZgroup_contextr   r   r   _run_request   s    zAIORateLimiter._run_request)r=   r>   r?   endpointdatarate_limit_argsr   c                    s$  |p| j }d}d}	|d}
|
dk	r(d}	ttt t|
}
W 5 Q R X t|
trZ|
dk sdt|
trh|
}t	|d D ]}zz$| j|	||||dI dH W W x  S  tk
r } zN||krtjd||d	  |jd
 }td| | j
  t|I dH  W 5 d}~X Y nX W 5 | j
  X qtdS )a  
        Processes a request by applying rate limiting.

        See :meth:`telegram.ext.BaseRateLimiter.process_request` for detailed information on the
        arguments.

        Args:
            rate_limit_args (:obj:`None` | :obj:`int`): If set, specifies the maximum number of
                retries to be made in case of a :exc:`~telegram.error.RetryAfter` exception.
                Defaults to :paramref:`AIORateLimiter.max_retries`.
        Fchat_idNTr   r    )r;   r<   r=   r>   r?   z*Rate limit hit after maximum of %d retries)exc_infog?z)Rate limit hit. Retrying after %f seconds)r   get
contextlibsuppress
ValueError	TypeErrorint
isinstancestrranger   r.   rA   r   _LOGGER	exceptionretry_afterinfoclearr,   sleep)r/   r=   r>   r?   rB   rC   rD   r'   r<   r;   rE   iexcrU   r   r   r   process_request   sB    

      

&zAIORateLimiter.process_request)r   r    r!   r"   r   )__name__
__module____qualname____doc__	__slots__floatrL   r0   r3   r4   r	   rN   boolr:   r   r   r   r   r   r   rA   r   rX   r   r   r   r   r   6   sB   @      "
"

)!r\   r,   rH   systypingr   r   r   r   r   r   r   r	   Z
aiolimiterr
   r*   ImportErrorZtelegram._utils.loggingr   Ztelegram._utils.typesr   Ztelegram.errorr   Ztelegram.ext._baseratelimiterr   version_infonullcontextr   asynccontextmanagerrY   rP   rL   r   r   r   r   r   <module>   s&   (

