From 667ca4d4f017edef2d3c1c89fd9d759cda24f7d6 Mon Sep 17 00:00:00 2001 From: Kenik Date: Wed, 24 Jun 2026 21:58:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B5=D0=B4=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B2=D1=81=D0=B5=D1=85=20?= =?UTF-8?q?=D0=BA=D0=B0=D1=80=D1=82=D0=B8=D0=BD=D0=BE=D0=BA=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=B5=20+=20=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=D0=BF=D0=BE=D1=82=D0=BE=D1=87=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B2=D0=B5=D0=B1-=D1=81=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _prewarm_media грузит ВСЕ *.jpg из help_img при старте (кроме bg.jpg), чтобы смена картинок услуг не подгружалась лениво в момент сообщения. - Веб-форма: ThreadingHTTPServer — медленный запрос не блокирует остальные; приём заявки сериализован _submit_lock (без гонок в SQLite/Excel). Co-Authored-By: Claude Opus 4.8 --- bot.py | 18 +++++++++--------- form/server.py | 12 ++++++++++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/bot.py b/bot.py index 3450187..cfca7ff 100644 --- a/bot.py +++ b/bot.py @@ -1194,15 +1194,15 @@ class VKBotApplication: try: peer = config.OPERATOR_PEER_ID base = os.path.join(os.path.dirname(__file__), "help_img") - files = ["Logo.jpg", "cs2.jpg", "dota2.jpg", "wot.jpg", - "career_player.jpg", "career_worker.jpg"] - # Загруженные админом картинки услуг (service_.jpg) — приоритетные для карусели - files += [os.path.basename(p) for p in glob.glob(os.path.join(base, "service_*.jpg"))] - for fn in files: - path = os.path.join(base, fn) - if os.path.exists(path): - self._upload_photo(peer, path) - logger.info("Медиа предзагружены в VK — карусель и логотип будут открываться быстро.") + # Грузим ВСЕ картинки из папки (кроме фона веб-формы), чтобы ничего + # не подгружалось лениво в момент сообщения. + count = 0 + for path in sorted(glob.glob(os.path.join(base, "*.jpg"))): + if os.path.basename(path).lower() == "bg.jpg": + continue + if self._upload_photo(peer, path): + count += 1 + logger.info(f"Медиа предзагружены в VK ({count} картинок) — открываются мгновенно.") except Exception as e: logger.error(f"Не удалось предзагрузить медиа: {e}") diff --git a/form/server.py b/form/server.py index 62b03cb..3be54db 100644 --- a/form/server.py +++ b/form/server.py @@ -6,7 +6,7 @@ import random import string import threading from collections import defaultdict -from http.server import HTTPServer, SimpleHTTPRequestHandler +from http.server import HTTPServer, ThreadingHTTPServer, SimpleHTTPRequestHandler from datetime import datetime import sys @@ -72,6 +72,8 @@ RATE_LIMIT_MAX = 5 RATE_LIMIT_WINDOW = 600 # секунд (10 минут) _rate_lock = threading.Lock() _rate_hits = defaultdict(list) +# Сериализует приём заявок (генерация ID + запись), чтобы при многопоточности не было гонок +_submit_lock = threading.Lock() def is_rate_limited(ip: str) -> bool: @@ -253,6 +255,11 @@ def gen_promo(): def add_registration(data): """Сохраняет заявку: SQLite (источник правды) → Excel (нестрого) → уведомления. Возвращает промокод.""" + with _submit_lock: + return _add_registration_locked(data) + + +def _add_registration_locked(data): current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") next_id = generate_next_id() # Промокод пока выключен флагом config.PROMO_ENABLED @@ -433,7 +440,8 @@ class FormHandler(SimpleHTTPRequestHandler): def run_server(port=8000): init_excel() # Pre-create Excel on startup server_address = ('', port) - httpd = HTTPServer(server_address, FormHandler) + # Многопоточный сервер: один медленный/зависший запрос не блокирует остальные. + httpd = ThreadingHTTPServer(server_address, FormHandler) print(f"\n=======================================================") print(f"Сервер ИКП запущен на http://localhost:{port}/") print(f"Вы можете открыть форму по адресу: http://localhost:{port}/index.html")