[ВОЛК] коррекция бота под повторные попытки
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline was successful

This commit is contained in:
2026-01-11 19:05:01 +03:00
parent b0f94c69e2
commit 7c4efdbc8e
+135 -47
View File
@@ -4,6 +4,7 @@ import time
import errno import errno
from dotenv import load_dotenv from dotenv import load_dotenv
import telebot import telebot
from telebot import apihelper
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
import pymysql import pymysql
from datetime import datetime, timezone from datetime import datetime, timezone
@@ -159,6 +160,11 @@ for name, value in required_vars.items():
logger.error(f"Требуется переменная окружения {name}") logger.error(f"Требуется переменная окружения {name}")
exit(1) exit(1)
# Настройка таймаутов для Telegram API
apihelper.READ_TIMEOUT = 120 # Таймаут на чтение ответа (увеличен для больших сообщений)
apihelper.CONNECT_TIMEOUT = 30 # Таймаут на подключение
apihelper.RETRY_ON_TIMEOUT = True # Автоматические повторные попытки при таймауте
# Создаем экземпляр бота # Создаем экземпляр бота
bot = telebot.TeleBot(RESPONDER_BOT_TOKEN) bot = telebot.TeleBot(RESPONDER_BOT_TOKEN)
@@ -802,44 +808,89 @@ def download_image(image_url):
return None return None
def send_post_message(chat_id, text, image_url=None, reply_markup=None): def send_post_message(chat_id, text, image_url=None, reply_markup=None):
"""Отправляет сообщение с изображением или текстом""" """Отправляет сообщение с изображением или текстом с повторными попытками при таймауте"""
logger.info(f"starting send_post_message") logger.info(f"starting send_post_message")
try:
# Дополнительная обрезка текста
if len(text) > UNIFIED_MAX_LENGTH:
text = text[:UNIFIED_MAX_LENGTH - 3] + "..."
if image_url: # Параметры для повторных попыток при таймауте
# Загружаем изображение max_retries = 5
image = download_image(image_url) retry_interval = 2
if image:
# Отправляем фото с подписью # Дополнительная обрезка текста
bot.send_photo( if len(text) > UNIFIED_MAX_LENGTH:
chat_id, text = text[:UNIFIED_MAX_LENGTH - 3] + "..."
photo=image,
caption=text, if image_url:
reply_markup=reply_markup, # Загружаем изображение
parse_mode='HTML' image = download_image(image_url)
) if image:
return True # Отправляем фото с подписью с повторными попытками
for attempt in range(1, max_retries + 1):
try:
bot.send_photo(
chat_id,
photo=image,
caption=text,
reply_markup=reply_markup,
parse_mode='HTML'
)
logger.info(f"Сообщение с изображением успешно отправлено (попытка {attempt})")
return True
except Exception as e:
error_str = str(e)
# Проверяем, является ли это таймаутом
if 'timeout' in error_str.lower() or 'timed out' in error_str.lower():
if attempt < max_retries:
logger.warning(f"Таймаут при отправке фото (попытка {attempt}/{max_retries}), повтор через {retry_interval} сек...")
time.sleep(retry_interval)
continue
else:
logger.error(f"Таймаут при отправке фото после {max_retries} попыток: {e}")
return False
else:
# Для других ошибок не повторяем
logger.error(f"Ошибка отправки фото: {e}")
return False
return False
else:
logger.warning(f"Не удалось загрузить изображение: {image_url}")
# Если изображение отсутствует или не загружено, отправляем текст с повторными попытками
for attempt in range(1, max_retries + 1):
try:
bot.send_message(
chat_id,
text,
reply_markup=reply_markup,
parse_mode='HTML'
)
logger.info(f"Текстовое сообщение успешно отправлено (попытка {attempt})")
return True
except Exception as e:
error_str = str(e)
# Проверяем, является ли это таймаутом
if 'timeout' in error_str.lower() or 'timed out' in error_str.lower():
if attempt < max_retries:
logger.warning(f"Таймаут при отправке сообщения (попытка {attempt}/{max_retries}), повтор через {retry_interval} сек...")
time.sleep(retry_interval)
continue
else:
logger.error(f"Таймаут при отправке сообщения после {max_retries} попыток: {e}")
return False
else: else:
logger.warning(f"Не удалось загрузить изображение: {image_url}") # Для других ошибок не повторяем
logger.error(f"Ошибка отправки сообщения: {e}")
return False
# Если изображение отсутствует или не загружено, отправляем текст return False
bot.send_message(
chat_id,
text,
reply_markup=reply_markup,
parse_mode='HTML'
)
return True
except Exception as e:
logger.error(f"Ошибка отправки сообщения: {e}")
return False
def send_direct_message(user_id, message_text): def send_direct_message(user_id, message_text):
"""Отправляет сообщение пользователю по ID с обработкой ошибок""" """Отправляет сообщение пользователю по ID с обработкой ошибок и повторными попытками при таймауте"""
logger.info(f"starting send_direct_message") logger.info(f"starting send_direct_message")
# Параметры для повторных попыток при таймауте
max_retries = 5
retry_interval = 2
try: try:
# Проверяем, что сообщение не пустое # Проверяем, что сообщение не пустое
if not message_text or not message_text.strip(): if not message_text or not message_text.strip():
@@ -848,17 +899,35 @@ def send_direct_message(user_id, message_text):
log_event("error", f"user_{user_id}", message=error_msg) log_event("error", f"user_{user_id}", message=error_msg)
return False, error_msg return False, error_msg
# Отправляем сообщение # Отправляем сообщение с повторными попытками
bot.send_message( for attempt in range(1, max_retries + 1):
user_id, try:
message_text, bot.send_message(
parse_mode='HTML' user_id,
) message_text,
parse_mode='HTML'
)
# Логируем успешную отправку # Логируем успешную отправку
log_event("info", f"user_{user_id}", message=f"Сообщение отправлено: {message_text[:50]}...") log_event("info", f"user_{user_id}", message=f"Сообщение отправлено: {message_text[:50]}...")
logger.info(f"Сообщение успешно отправлено пользователю {user_id}") logger.info(f"Сообщение успешно отправлено пользователю {user_id} (попытка {attempt})")
return True, "Сообщение отправлено успешно" return True, "Сообщение отправлено успешно"
except Exception as e:
error_str = str(e)
# Проверяем, является ли это таймаутом
if 'timeout' in error_str.lower() or 'timed out' in error_str.lower():
if attempt < max_retries:
logger.warning(f"Таймаут при отправке сообщения пользователю {user_id} (попытка {attempt}/{max_retries}), повтор через {retry_interval} сек...")
time.sleep(retry_interval)
continue
else:
error_msg = f"Таймаут при отправке сообщения после {max_retries} попыток: {e}"
logger.error(error_msg)
log_event("error", f"user_{user_id}", message=error_msg)
return False, f"Таймаут при отправке сообщения. Попробуйте позже."
else:
# Для других ошибок выходим из цикла и обрабатываем дальше
break
except telebot.apihelper.ApiTelegramException as e: except telebot.apihelper.ApiTelegramException as e:
error_msg = f"Ошибка Telegram API: {e}" error_msg = f"Ошибка Telegram API: {e}"
@@ -963,12 +1032,31 @@ def handle_start(message):
# Команда /start без параметров # Команда /start без параметров
welcome_text = get_welcome_text() welcome_text = get_welcome_text()
bot.send_message( # Отправляем приветственное сообщение с повторными попытками при таймауте
message.chat.id, max_retries = 5
welcome_text, retry_interval = 2
reply_markup=create_main_keyboard(), for attempt in range(1, max_retries + 1):
parse_mode='HTML' try:
) bot.send_message(
message.chat.id,
welcome_text,
reply_markup=create_main_keyboard(),
parse_mode='HTML'
)
logger.info(f"Приветственное сообщение отправлено (попытка {attempt})")
break
except Exception as e:
error_str = str(e)
if 'timeout' in error_str.lower() or 'timed out' in error_str.lower():
if attempt < max_retries:
logger.warning(f"Таймаут при отправке приветственного сообщения (попытка {attempt}/{max_retries}), повтор через {retry_interval} сек...")
time.sleep(retry_interval)
continue
else:
logger.error(f"Таймаут при отправке приветственного сообщения после {max_retries} попыток: {e}")
raise
else:
raise
except Exception as e: except Exception as e:
error_msg = f"Ошибка в обработке /start: {e}" error_msg = f"Ошибка в обработке /start: {e}"
logger.error(error_msg) logger.error(error_msg)