a
    %ßik  ã                   @   s†   d Z ddlmZ ddlmZmZ ddlmZ ddlmZ G dd„ dƒZeej	dd	�Z
eejd
d	�Zeedœdd„Zeedœdd„ZdS )z\
Rate limiting for expensive operations.

Uses token bucket algorithm to prevent API abuse.
é    )Údefaultdict)ÚdatetimeÚ	timedelta)ÚDict)Úconfigc                   @   sN   e Zd ZdZeedœdd„Zeedœdd„Zeedœdd	„Z	ed
œdd„Z
dS )ÚRateLimiterzí
    Token bucket rate limiter.

    Usage:
        limiter = RateLimiter(max_requests=60, window_seconds=3600)
        if limiter.is_allowed('user_123'):
            # Allow request
        else:
            # Reject (rate limited)
    ©Úmax_requestsÚwindow_secondsc                 C   s    || _ t|d�| _ttƒ| _dS )z©
        Initialize rate limiter.

        Args:
            max_requests: Maximum requests allowed in window
            window_seconds: Time window in seconds
        )ÚsecondsN)r	   r   Úwindowr   ÚlistÚrequests)Úselfr	   r
   © r   ú-/var/www/lichun.app/lichun/ws/rate_limiter.pyÚ__init__   s    zRateLimiter.__init__)Ú
identifierÚreturnc                    sj   t  ¡ }|| j ‰ ‡ fdd„| j| D ƒ| j|< t| j| ƒ| jkrVtd|› �ƒ dS | j|  |¡ dS )z¶
        Check if request is allowed.

        Args:
            identifier: User ID or other identifier

        Returns:
            True if allowed, False if rate limited
        c                    s   g | ]}|ˆ kr|‘qS r   r   ©Ú.0Zreq_time©Úcutoffr   r   Ú
<listcomp>3   s   ÿz*RateLimiter.is_allowed.<locals>.<listcomp>zRate limit exceeded for FT)r   Únowr   r   Úlenr	   ÚprintÚappend©r   r   r   r   r   r   Ú
is_allowed%   s    


ÿzRateLimiter.is_allowedc                    sJ   t  ¡ }|| j ‰ ‡ fdd„| j| D ƒ| j|< td| jt| j| ƒ ƒS )z¹
        Get remaining requests in current window.

        Args:
            identifier: User ID or other identifier

        Returns:
            Number of requests remaining
        c                    s   g | ]}|ˆ kr|‘qS r   r   r   r   r   r   r   O   s   ÿz-RateLimiter.get_remaining.<locals>.<listcomp>r   )r   r   r   r   Úmaxr	   r   r   r   r   r   Úget_remainingA   s    


ÿzRateLimiter.get_remaining)r   c                 C   s   || j v r| j |= dS )zReset rate limit for identifierN)r   )r   r   r   r   r   ÚresetV   s    
zRateLimiter.resetN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úintr   ÚstrÚboolr   r!   r"   r   r   r   r   r      s
   r   i  r   é<   )Úuser_idr   c                 C   s0   t  | ¡s,t  | ¡}td| › d|› �ƒ dS dS )z‘
    Check if user can make OpenAI request.

    Args:
        user_id: User ID

    Returns:
        True if allowed, False if rate limited
    zOpenAI rate limit exceeded for z. Remaining: FT)Úopenai_limiterr   r!   r   )r+   Ú	remainingr   r   r   Úcheck_openai_rate_limith   s
    


r.   c                 C   s    t  | ¡std| › �ƒ dS dS )z”
    Check if user can send WebSocket message.

    Args:
        user_id: User ID

    Returns:
        True if allowed, False if rate limited
    z"WebSocket rate limit exceeded for FT)Úwebsocket_limiterr   r   )r+   r   r   r   Úcheck_websocket_rate_limity   s    

r0   N)r&   Úcollectionsr   r   r   Útypingr   r   r   ÚOPENAI_MAX_REQUESTS_PER_HOURr,   Ú!WEBSOCKET_MAX_MESSAGES_PER_MINUTEr/   r(   r)   r.   r0   r   r   r   r   Ú<module>   s   Pþþ