63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
"""Общая координация задержек и автоопределение бана."""
|
|
|
|
import time, threading, logging
|
|
import requests
|
|
from .config import DELAY, TIMEOUT, HEADERS
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Throttle:
|
|
"""Thread-safe координатор задержек."""
|
|
|
|
def __init__(self, delay: float = DELAY, enabled: bool = True):
|
|
self._delay = delay
|
|
self._enabled = enabled
|
|
self._lock = threading.Lock()
|
|
self._last = 0.0
|
|
|
|
@property
|
|
def enabled(self) -> bool:
|
|
return self._enabled
|
|
|
|
def wait(self):
|
|
if not self._enabled:
|
|
time.sleep(self._delay)
|
|
return
|
|
with self._lock:
|
|
elapsed = time.monotonic() - self._last
|
|
if elapsed < self._delay:
|
|
time.sleep(self._delay - elapsed)
|
|
self._last = time.monotonic()
|
|
|
|
def disable(self):
|
|
self._enabled = False
|
|
|
|
|
|
class BanDetector:
|
|
"""Проверяет, банит ли сервер."""
|
|
|
|
@staticmethod
|
|
def test(base_url: str, count: int = 5) -> bool:
|
|
log.info(f"🔍 Проверка бана: {count} запросов подряд...")
|
|
s = requests.Session()
|
|
s.headers.update(HEADERS)
|
|
banned = False
|
|
for i in range(count):
|
|
try:
|
|
r = s.get(base_url, timeout=TIMEOUT)
|
|
log.info(f" [{i+1}/{count}] HTTP {r.status_code}")
|
|
if r.status_code in (403, 429):
|
|
banned = True
|
|
break
|
|
except Exception as e:
|
|
log.warning(f" [{i+1}/{count}] ошибка: {e}")
|
|
banned = True
|
|
break
|
|
if banned:
|
|
log.warning("⚠️ Сервер банит быстрые запросы — включаю координацию")
|
|
else:
|
|
log.info("✅ Бан не обнаружен")
|
|
s.close()
|
|
return banned
|