Add request flood restriction

This commit is contained in:
luoshuijs 2023-06-07 23:33:33 +08:00 committed by GitHub
parent 4b26982b3a
commit 8cdab782a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 0 deletions

View File

@ -24,6 +24,7 @@ from core.config import config as application_config
from core.handler.limiterhandler import LimiterHandler from core.handler.limiterhandler import LimiterHandler
from core.manager import Managers from core.manager import Managers
from core.override.telegram import HTTPXRequest from core.override.telegram import HTTPXRequest
from core.ratelimiter import RateLimiter
from utils.const import WRAPPER_ASSIGNMENTS from utils.const import WRAPPER_ASSIGNMENTS
from utils.log import logger from utils.log import logger
from utils.models.signal import Singleton from utils.models.signal import Singleton
@ -76,6 +77,7 @@ class Application(Singleton):
pool_timeout=application_config.pool_timeout, pool_timeout=application_config.pool_timeout,
) )
) )
.rate_limiter(RateLimiter())
.build() .build()
) )
web_server = Server( web_server = Server(

67
core/ratelimiter.py Normal file
View File

@ -0,0 +1,67 @@
import asyncio
import contextlib
from typing import Callable, Coroutine, Any, Union, List, Dict, Optional, TypeVar, Type
from telegram.error import RetryAfter
from telegram.ext import BaseRateLimiter, ApplicationHandlerStop
from utils.log import logger
JSONDict: Type[dict[str, Any]] = Dict[str, Any]
RL_ARGS = TypeVar("RL_ARGS")
class RateLimiter(BaseRateLimiter[int]):
_lock = asyncio.Lock()
__slots__ = (
"_limiter_info",
"_retry_after_event",
)
def __init__(self):
self._limiter_info: Dict[Union[str, int], float] = {}
self._retry_after_event = asyncio.Event()
self._retry_after_event.set()
async def process_request(
self,
callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]],
args: Any,
kwargs: Dict[str, Any],
endpoint: str,
data: Dict[str, Any],
rate_limit_args: Optional[RL_ARGS],
) -> Union[bool, JSONDict, List[JSONDict]]:
chat_id = data.get("chat_id")
with contextlib.suppress(ValueError, TypeError):
chat_id = int(chat_id)
loop = asyncio.get_running_loop()
time = loop.time()
await self._retry_after_event.wait()
async with self._lock:
chat_limit_time = self._limiter_info.get(chat_id)
if chat_limit_time:
if time >= chat_limit_time:
raise ApplicationHandlerStop
del self._limiter_info[chat_id]
try:
return await callback(*args, **kwargs)
except RetryAfter as exc:
logger.warning("chat_id[%s] 触发洪水限制 当前被服务器限制 retry_after[%s]秒", chat_id, exc.retry_after)
self._limiter_info[chat_id] = time + (exc.retry_after * 2)
sleep = exc.retry_after + 0.1
self._retry_after_event.clear()
await asyncio.sleep(sleep)
finally:
self._retry_after_event.set()
async def initialize(self) -> None:
pass
async def shutdown(self) -> None:
pass