From 8c3a62d021d259c6f7d2d3db982c3deb17f5385c Mon Sep 17 00:00:00 2001 From: gitadmin Date: Sun, 2 Aug 2026 15:05:14 +0300 Subject: [PATCH] =?UTF-8?q?[ZILANT]=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20=D0=B7=D0=B0=D1=86=D0=B8=D0=BA=D0=BB=D0=B8=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=B5=D0=B1=D1=85=D1=83=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gunicorn_bot.conf.py | 16 +++++++++ services/testbot-git.service | 2 +- tg_mainbot.py | 68 ++++++++++++++++++++++++------------ wsgi_bot.py | 28 ++++----------- 4 files changed, 68 insertions(+), 46 deletions(-) create mode 100644 gunicorn_bot.conf.py diff --git a/gunicorn_bot.conf.py b/gunicorn_bot.conf.py new file mode 100644 index 0000000..2f87186 --- /dev/null +++ b/gunicorn_bot.conf.py @@ -0,0 +1,16 @@ +"""Конфиг gunicorn для responder-бота. + +Webhook регистрируется один раз в master (on_starting), а не в каждом worker — +иначе 4 воркера параллельно бьют setWebhook и ловят 429. +""" + + +def on_starting(server): + from tg_mainbot import setup_webhook + + server.log.info("gunicorn on_starting: регистрация Telegram webhook через ретранслятор") + setup_webhook() + + +# На случай, если webhook уже стоит, а воркеры перезапускаются — не трогаем. +# Повторная регистрация только при старте master-процесса. diff --git a/services/testbot-git.service b/services/testbot-git.service index eaf83d7..5be25dc 100644 --- a/services/testbot-git.service +++ b/services/testbot-git.service @@ -6,7 +6,7 @@ After=network.target [Service] User=root WorkingDirectory=/opt/testbot-git -ExecStart=/opt/testbot-git/venv/bin/gunicorn --bind 192.168.0.109:5005 --workers 4 wsgi_bot:app +ExecStart=/opt/testbot-git/venv/bin/gunicorn --bind 192.168.0.109:5005 --workers 4 --config gunicorn_bot.conf.py wsgi_bot:app Restart=always [Install] diff --git a/tg_mainbot.py b/tg_mainbot.py index 9c43301..1a8e83c 100644 --- a/tg_mainbot.py +++ b/tg_mainbot.py @@ -1434,32 +1434,53 @@ def handle_callback(call): def setup_webhook(): """Настройка вебхука через ретранслятор (доставка на WEBHOOK_URL).""" - logger.info(f"starting setup_webhook") - try: - # Удаляем предыдущий вебхук - bot.remove_webhook() + logger.info("starting setup_webhook") + last_error = None + # setWebhook сам заменяет предыдущий URL; deleteWebhook не нужен и удваивает нагрузку на API. + for attempt in range(1, 6): + try: + bot.set_webhook( + url=WEBHOOK_URL, + secret_token=WEBHOOK_SECRET, + max_connections=40, + ) + logger.info( + f"Вебхук установлен через ретранслятор {get_relay_url()}: " + f"локальная доставка {WEBHOOK_URL}" + ) + logger.info(f"Секретный токен: {'установлен' if WEBHOOK_SECRET else 'не установлен'}") + logger.info(f"Прослушивание порта: {WEBHOOK_PORT}") + logger.info(f"Путь вебхука: {WEBHOOK_PATH}") + log_event("system", "system", message=f"Вебхук установлен на {WEBHOOK_URL}") + return + except Exception as e: + last_error = e + retry_after = _webhook_retry_after(e) + if retry_after is not None and attempt < 5: + logger.warning( + f"setup_webhook: попытка {attempt}/5 — {e}; ждём {retry_after} с" + ) + time.sleep(retry_after) + continue + break - # Устанавливаем новый вебхук: - # relay сохранит WEBHOOK_URL как адрес локальной доставки и зарегистрирует - # внешний URL на Telegram (external_relay). - bot.set_webhook( - url=WEBHOOK_URL, - secret_token=WEBHOOK_SECRET, - max_connections=40 - ) - logger.info(f"Вебхук установлен через ретранслятор {get_relay_url()}: локальная доставка {WEBHOOK_URL}") - logger.info(f"Секретный токен: {'установлен' if WEBHOOK_SECRET else 'не установлен'}") - logger.info(f"Прослушивание порта: {WEBHOOK_PORT}") - logger.info(f"Путь вебхука: {WEBHOOK_PATH}") + error_msg = f"Ошибка настройка вебхука: {last_error}" + logger.error(error_msg) + log_event("error", "system", message=error_msg) + raise RuntimeError(error_msg) from last_error - # Логируем успешную настройку вебхука - log_event("system", "system", message=f"Вебхук установлен на {WEBHOOK_URL}") - except Exception as e: - error_msg = f"Ошибка настройка вебхука: {e}" - logger.error(error_msg) - log_event("error", "system", message=error_msg) - raise RuntimeError(error_msg) from e +def _webhook_retry_after(exc): + """Извлекает секунды ожидания из 429 / 'retry after N', иначе None.""" + text = str(exc) + if "429" not in text and "Too Many Requests" not in text and "retry after" not in text.lower(): + return None + import re + match = re.search(r"retry after (\d+)", text, re.IGNORECASE) + if match: + return max(int(match.group(1)), 1) + 1 + return 2 + # Используем путь из WEBHOOK_URL @app.route(WEBHOOK_PATH, methods=['POST']) @@ -1474,6 +1495,7 @@ def webhook(): try: update = telebot.types.Update.de_json(json_data) bot.process_new_updates([update]) + return "OK", 200 except Exception as e: error_msg = f"Ошибка обработки вебхука: {e}" diff --git a/wsgi_bot.py b/wsgi_bot.py index 3feab50..115596f 100644 --- a/wsgi_bot.py +++ b/wsgi_bot.py @@ -1,26 +1,10 @@ -import fcntl -import os +from tg_mainbot import app -from tg_mainbot import app, setup_webhook - - -def _setup_webhook_once(): - """ - Регистрируем webhook один раз при старте gunicorn. - Без этого (__main__ не вызывается) Telegram не шлёт апдейты через relay — - ссылка «Подписка» (/start) молчит. - """ - lock_path = os.path.join(os.getenv("TMPDIR", "/tmp"), "zilant_webhook_setup.lock") - with open(lock_path, "w") as lock_file: - try: - fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) - except BlockingIOError: - # Другой воркер уже ставит webhook - return - setup_webhook() - - -_setup_webhook_once() +# Регистрация webhook — в gunicorn_bot.conf.py (on_starting), один раз в master. +# Здесь только WSGI-приложение для воркеров. if __name__ == "__main__": + from tg_mainbot import setup_webhook + + setup_webhook() app.run()