[ZILANT] убрать зацикливание запроса на вебхук
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-08-02 15:05:14 +03:00
parent 064d22cfd8
commit 8c3a62d021
4 changed files with 68 additions and 46 deletions
+16
View File
@@ -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-процесса.
+1 -1
View File
@@ -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]
+45 -23
View File
@@ -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}"
+6 -22
View File
@@ -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()