import os import logging import httpx import asyncio import time import sys import json from datetime import datetime from dotenv import load_dotenv from telegram_relay import bot_api_method_url, get_relay_url from season_links import subscription_start_link # Загрузка переменных окружения load_dotenv() # Настройки из переменных окружения POSTER_BOT_TOKEN = os.getenv('POSTER_BOT_TOKEN') RESPONDER_BOT_NAME = os.getenv('RESPONDER_BOT_NAME') CHANNEL_ID = os.getenv('CHANNEL_ID') DESC_PREFIX = os.getenv('DESC_PREFIX', '') PZK_PREFIX = os.getenv('PZK_PREFIX', '') MAX_CAPTION_LENGTH = int(os.getenv('MAX_CAPTION_LENGTH', 1000)) MAX_TEXT_LENGTH = int(os.getenv('MAX_TEXT_LENGTH', 4000)) PUBLISH_SILENTLY = os.getenv('PUBLISH_SILENTLY', 'false').lower() in ('true', '1', 'yes', 'on') USE_SUBSCRIPTION_BOT = os.getenv('USE_SUBSCRIPTION_BOT', 'true').lower() in ('true', '1', 'yes', 'on') # Настройка логирования LOG_FILE = 'volk_telegram_testing.log' logger = logging.getLogger('VOLK_TG_Test') logger.setLevel(logging.DEBUG) formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S') # Консольный обработчик console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # Файловый обработчик с повторными попытками class RetryFileHandler(logging.FileHandler): def emit(self, record): for _ in range(5): try: super().emit(record) return except (IOError, PermissionError): time.sleep(0.5) print(f"Failed to write to log file after 5 attempts: {record.msg}") file_handler = RetryFileHandler(LOG_FILE, encoding='utf-8') file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(formatter) logger.addHandler(file_handler) async def test_telegram_publish(): """Тестовая публикация сообщения в Telegram""" logger.info("=" * 80) logger.info("НАЧАЛО ТЕСТОВОЙ ПУБЛИКАЦИИ В TELEGRAM") logger.info("=" * 80) # Логируем загруженные параметры logger.info("Загруженные параметры из .env:") logger.info(f" POSTER_BOT_TOKEN: {'*' * 20 if POSTER_BOT_TOKEN else 'НЕ ЗАДАН'}") logger.info(f" RESPONDER_BOT_NAME: {RESPONDER_BOT_NAME}") logger.info(f" CHANNEL_ID: {CHANNEL_ID}") logger.info(f" DESC_PREFIX: {DESC_PREFIX}") logger.info(f" PZK_PREFIX: {PZK_PREFIX}") logger.info(f" MAX_CAPTION_LENGTH: {MAX_CAPTION_LENGTH}") logger.info(f" MAX_TEXT_LENGTH: {MAX_TEXT_LENGTH}") logger.info(f" PUBLISH_SILENTLY: {PUBLISH_SILENTLY}") logger.info(f" USE_SUBSCRIPTION_BOT: {USE_SUBSCRIPTION_BOT}") logger.info(f" RELAY_URL: {get_relay_url()}") # Проверка обязательных параметров if not POSTER_BOT_TOKEN: logger.error("ОШИБКА: POSTER_BOT_TOKEN не задан в .env") return False if not CHANNEL_ID: logger.error("ОШИБКА: CHANNEL_ID не задан в .env") return False # Формируем тестовое сообщение test_number = "TEST001" test_tags = "#тест #volk_testing" test_unit_name = "Тестовая площадка" test_name = "Тестовое событие для проверки публикации" test_about = "Это тестовое сообщение для проверки работы публикации в Telegram. " * 5 # Формируем базовый текст base_text = f"{test_tags}\n{test_unit_name}\n{test_name}" base_text_length = len(base_text) # Формируем строку ссылок placeholder_message_id = '1234567890' if USE_SUBSCRIPTION_BOT: links_line = ( f'🔎 Инфо | ' f'📌 Иду | ' f'🔜 Подписка' ) else: links_line = f'🔎 Инфо' links_line_length = len(links_line) # Определяем максимальную длину max_length = MAX_CAPTION_LENGTH # Используем caption для теста # Вычисляем доступную длину для текста available_length = max_length - base_text_length - links_line_length - 3 if available_length < 0: available_length = 0 # Обрезаем about до доступной длины if len(test_about) > available_length: test_about = test_about[:available_length-3] + "..." # Формируем финальный текст final_text = f"{base_text}\n\n{test_about}\n\n{links_line}" # Логируем детали сообщения logger.info("=" * 80) logger.info("ДЕТАЛИ ТЕСТОВОГО СООБЩЕНИЯ:") logger.info("=" * 80) logger.info(f"Длина base_text: {base_text_length} символов") logger.info(f"Длина test_about: {len(test_about)} символов") logger.info(f"Длина links_line: {links_line_length} символов") logger.info(f"Максимальная длина (max_length): {max_length} символов") logger.info(f"Доступная длина для about: {available_length} символов") logger.info(f"Длина final_text: {len(final_text)} символов") logger.info(f"Используется изображение: НЕТ") logger.info("=" * 80) logger.info("ТЕКСТ СООБЩЕНИЯ:") logger.info("=" * 80) logger.info(final_text) logger.info("=" * 80) # Создаем httpx клиент для прямых запросов к Telegram Bot API httpx_client = None try: # Создаём клиент БЕЗ HTTP/2 с указанными параметрами logger.info("Создание httpx клиента с отключенным HTTP/2...") httpx_client = httpx.AsyncClient( http2=False, # ← отключаем HTTP/2 timeout=20.0, # явный таймаут follow_redirects=True ) logger.info("httpx клиент успешно создан с http2=False, timeout=20.0") # Получаем информацию о боте через Telegram Bot API try: logger.info("=" * 80) logger.info("ПОПЫТКА ПОЛУЧЕНИЯ ИНФОРМАЦИИ О БОТЕ") logger.info("=" * 80) get_me_url = bot_api_method_url(POSTER_BOT_TOKEN, "getMe") logger.info(f"URL запроса: {get_me_url}") logger.info(f"Метод: GET") start_time = datetime.now() response = await httpx_client.get(get_me_url) end_time = datetime.now() duration = (end_time - start_time).total_seconds() logger.info(f"Статус ответа: {response.status_code}") logger.info(f"Время запроса: {duration:.2f} секунд") if response.status_code == 200: bot_info = response.json() if bot_info.get('ok'): result = bot_info.get('result', {}) logger.info("=" * 80) logger.info("ИНФОРМАЦИЯ О БОТЕ УСПЕШНО ПОЛУЧЕНА!") logger.info("=" * 80) logger.info(f"ID: {result.get('id')}") logger.info(f"Username: @{result.get('username')}") logger.info(f"First name: {result.get('first_name')}") logger.info(f"Is bot: {result.get('is_bot')}") logger.info("=" * 80) else: logger.error(f"Ошибка в ответе API: {bot_info}") else: logger.error(f"HTTP ошибка: {response.status_code}") logger.error(f"Текст ответа: {response.text}") except httpx.TimeoutException as e: logger.error("=" * 80) logger.error("ТАЙМАУТ при получении информации о боте!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except httpx.HTTPError as e: logger.error("=" * 80) logger.error("ОШИБКА HTTP при получении информации о боте!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except Exception as e: logger.error("=" * 80) logger.error("НЕОЖИДАННАЯ ОШИБКА при получении информации о боте!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e) if hasattr(e, '__dict__') else 'N/A'}") logger.error(f"Полное представление: {repr(e)}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") # Публикуем тестовое сообщение через Telegram Bot API try: logger.info("=" * 80) logger.info("ПОПЫТКА ОТПРАВКИ ТЕСТОВОГО СООБЩЕНИЯ") logger.info("=" * 80) send_message_url = bot_api_method_url(POSTER_BOT_TOKEN, "sendMessage") logger.info(f"URL запроса: {send_message_url}") logger.info(f"Метод: POST") logger.info(f"Параметры запроса:") logger.info(f" chat_id: {CHANNEL_ID}") logger.info(f" text_length: {len(final_text)}") logger.info(f" parse_mode: HTML") logger.info(f" disable_notification: {PUBLISH_SILENTLY}") logger.info(f" disable_web_page_preview: True") payload = { 'chat_id': CHANNEL_ID, 'text': final_text, 'parse_mode': 'HTML', 'disable_notification': PUBLISH_SILENTLY, 'disable_web_page_preview': True } start_time = datetime.now() response = await httpx_client.post(send_message_url, json=payload) end_time = datetime.now() duration = (end_time - start_time).total_seconds() logger.info(f"Статус ответа: {response.status_code}") logger.info(f"Время запроса: {duration:.2f} секунд") if response.status_code == 200: result = response.json() if result.get('ok'): message_data = result.get('result', {}) message_id = message_data.get('message_id') chat_data = message_data.get('chat', {}) logger.info("=" * 80) logger.info("СООБЩЕНИЕ УСПЕШНО ОТПРАВЛЕНО!") logger.info("=" * 80) logger.info(f"Message ID: {message_id}") logger.info(f"Chat ID: {chat_data.get('id')}") logger.info(f"Chat type: {chat_data.get('type')}") logger.info(f"Chat title: {chat_data.get('title', 'N/A')}") logger.info(f"Время отправки: {duration:.2f} секунд") logger.info(f"Дата сообщения: {message_data.get('date')}") logger.info("=" * 80) # Редактируем сообщение, добавляя информацию о редактировании try: logger.info("=" * 80) logger.info("ПОПЫТКА РЕДАКТИРОВАНИЯ СООБЩЕНИЯ") logger.info("=" * 80) edit_message_url = bot_api_method_url(POSTER_BOT_TOKEN, "editMessageText") logger.info(f"URL запроса: {edit_message_url}") logger.info(f"Метод: POST") logger.info(f"Параметры запроса:") logger.info(f" chat_id: {CHANNEL_ID}") logger.info(f" message_id: {message_id}") # Добавляем информацию о редактировании в конец сообщения edited_text = f"{final_text}\n\nСообщение отредактировано через httpx клиент" logger.info(f" text_length (после редактирования): {len(edited_text)}") logger.info(f" parse_mode: HTML") edit_payload = { 'chat_id': CHANNEL_ID, 'message_id': message_id, 'text': edited_text, 'parse_mode': 'HTML', 'disable_web_page_preview': True } edit_start_time = datetime.now() edit_response = await httpx_client.post(edit_message_url, json=edit_payload) edit_end_time = datetime.now() edit_duration = (edit_end_time - edit_start_time).total_seconds() logger.info(f"Статус ответа: {edit_response.status_code}") logger.info(f"Время запроса: {edit_duration:.2f} секунд") if edit_response.status_code == 200: edit_result = edit_response.json() if edit_result.get('ok'): logger.info("=" * 80) logger.info("СООБЩЕНИЕ УСПЕШНО ОТРЕДАКТИРОВАНО!") logger.info("=" * 80) logger.info(f"Время редактирования: {edit_duration:.2f} секунд") logger.info("=" * 80) else: logger.error(f"Ошибка в ответе API при редактировании: {edit_result}") else: logger.error(f"HTTP ошибка при редактировании: {edit_response.status_code}") logger.error(f"Текст ответа: {edit_response.text}") except httpx.TimeoutException as e: logger.error("=" * 80) logger.error("ТАЙМАУТ ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") logger.error(f"Параметры запроса:") logger.error(f" chat_id: {CHANNEL_ID}") logger.error(f" message_id: {message_id}") logger.error(f" text_length: {len(edited_text)}") except httpx.HTTPError as e: logger.error("=" * 80) logger.error("ОШИБКА HTTP ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except Exception as e: logger.error("=" * 80) logger.error("НЕОЖИДАННАЯ ОШИБКА ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e) if hasattr(e, '__dict__') else 'N/A'}") logger.error(f"Полное представление: {repr(e)}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") # Публикуем второе сообщение с картинкой try: logger.info("=" * 80) logger.info("ПОПЫТКА ОТПРАВКИ ТЕСТОВОГО СООБЩЕНИЯ С КАРТИНКОЙ") logger.info("=" * 80) send_photo_url = bot_api_method_url(POSTER_BOT_TOKEN, "sendPhoto") image_url = "https://base.volk-nn.ru/uploads/events/volk26/volk26_event/20251210_175059_0.jpg" # Формируем текст для сообщения с картинкой photo_caption = f"{test_tags}\n{test_unit_name}\nТестовое сообщение с картинкой\n\nЭто тестовое сообщение с изображением для проверки работы публикации фото в Telegram." logger.info(f"URL запроса: {send_photo_url}") logger.info(f"Метод: POST") logger.info(f"Параметры запроса:") logger.info(f" chat_id: {CHANNEL_ID}") logger.info(f" photo_url: {image_url}") logger.info(f" photo_url_length: {len(image_url)}") logger.info(f" caption_length: {len(photo_caption)}") logger.info(f" parse_mode: HTML") logger.info(f" disable_notification: {PUBLISH_SILENTLY}") photo_payload = { 'chat_id': CHANNEL_ID, 'photo': image_url, 'caption': photo_caption, 'parse_mode': 'HTML', 'disable_notification': PUBLISH_SILENTLY } photo_start_time = datetime.now() photo_response = await httpx_client.post(send_photo_url, json=photo_payload) photo_end_time = datetime.now() photo_duration = (photo_end_time - photo_start_time).total_seconds() logger.info(f"Статус ответа: {photo_response.status_code}") logger.info(f"Время запроса: {photo_duration:.2f} секунд") if photo_response.status_code == 200: photo_result = photo_response.json() if photo_result.get('ok'): photo_message_data = photo_result.get('result', {}) photo_message_id = photo_message_data.get('message_id') photo_chat_data = photo_message_data.get('chat', {}) logger.info("=" * 80) logger.info("СООБЩЕНИЕ С КАРТИНКОЙ УСПЕШНО ОТПРАВЛЕНО!") logger.info("=" * 80) logger.info(f"Message ID: {photo_message_id}") logger.info(f"Chat ID: {photo_chat_data.get('id')}") logger.info(f"Chat type: {photo_chat_data.get('type')}") logger.info(f"Chat title: {photo_chat_data.get('title', 'N/A')}") logger.info(f"Время отправки: {photo_duration:.2f} секунд") logger.info(f"Дата сообщения: {photo_message_data.get('date')}") logger.info("=" * 80) # Редактируем сообщение с картинкой, добавляя информацию о редактировании try: logger.info("=" * 80) logger.info("ПОПЫТКА РЕДАКТИРОВАНИЯ СООБЩЕНИЯ С КАРТИНКОЙ") logger.info("=" * 80) edit_photo_caption_url = bot_api_method_url(POSTER_BOT_TOKEN, "editMessageCaption") logger.info(f"URL запроса: {edit_photo_caption_url}") logger.info(f"Метод: POST") logger.info(f"Параметры запроса:") logger.info(f" chat_id: {CHANNEL_ID}") logger.info(f" message_id: {photo_message_id}") # Добавляем информацию о редактировании в конец подписи edited_photo_caption = f"{photo_caption}\n\nСообщение с картинкой отредактировано через httpx клиент" logger.info(f" caption_length (после редактирования): {len(edited_photo_caption)}") logger.info(f" parse_mode: HTML") edit_photo_payload = { 'chat_id': CHANNEL_ID, 'message_id': photo_message_id, 'caption': edited_photo_caption, 'parse_mode': 'HTML' } edit_photo_start_time = datetime.now() edit_photo_response = await httpx_client.post(edit_photo_caption_url, json=edit_photo_payload) edit_photo_end_time = datetime.now() edit_photo_duration = (edit_photo_end_time - edit_photo_start_time).total_seconds() logger.info(f"Статус ответа: {edit_photo_response.status_code}") logger.info(f"Время запроса: {edit_photo_duration:.2f} секунд") if edit_photo_response.status_code == 200: edit_photo_result = edit_photo_response.json() if edit_photo_result.get('ok'): logger.info("=" * 80) logger.info("СООБЩЕНИЕ С КАРТИНКОЙ УСПЕШНО ОТРЕДАКТИРОВАНО!") logger.info("=" * 80) logger.info(f"Время редактирования: {edit_photo_duration:.2f} секунд") logger.info("=" * 80) else: logger.error(f"Ошибка в ответе API при редактировании подписи: {edit_photo_result}") logger.error(f"Описание ошибки: {edit_photo_result.get('description', 'N/A')}") logger.error(f"Код ошибки: {edit_photo_result.get('error_code', 'N/A')}") else: logger.error(f"HTTP ошибка при редактировании подписи: {edit_photo_response.status_code}") logger.error(f"Текст ответа: {edit_photo_response.text}") except httpx.TimeoutException as e: logger.error("=" * 80) logger.error("ТАЙМАУТ ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") logger.error(f"Параметры запроса:") logger.error(f" chat_id: {CHANNEL_ID}") logger.error(f" message_id: {photo_message_id}") logger.error(f" caption_length: {len(edited_photo_caption)}") except httpx.HTTPError as e: logger.error("=" * 80) logger.error("ОШИБКА HTTP ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except Exception as e: logger.error("=" * 80) logger.error("НЕОЖИДАННАЯ ОШИБКА ПРИ РЕДАКТИРОВАНИИ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e) if hasattr(e, '__dict__') else 'N/A'}") logger.error(f"Полное представление: {repr(e)}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") else: logger.error(f"Ошибка в ответе API при отправке фото: {photo_result}") logger.error(f"Описание ошибки: {photo_result.get('description', 'N/A')}") logger.error(f"Код ошибки: {photo_result.get('error_code', 'N/A')}") else: logger.error(f"HTTP ошибка при отправке фото: {photo_response.status_code}") logger.error(f"Текст ответа: {photo_response.text}") except httpx.TimeoutException as e: logger.error("=" * 80) logger.error("ТАЙМАУТ ПРИ ОТПРАВКЕ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") logger.error(f"Параметры запроса:") logger.error(f" chat_id: {CHANNEL_ID}") logger.error(f" photo_url: {image_url}") logger.error(f" caption_length: {len(photo_caption)}") except httpx.HTTPError as e: logger.error("=" * 80) logger.error("ОШИБКА HTTP ПРИ ОТПРАВКЕ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except Exception as e: logger.error("=" * 80) logger.error("НЕОЖИДАННАЯ ОШИБКА ПРИ ОТПРАВКЕ СООБЩЕНИЯ С КАРТИНКОЙ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e) if hasattr(e, '__dict__') else 'N/A'}") logger.error(f"Полное представление: {repr(e)}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") return True else: logger.error(f"Ошибка в ответе API: {result}") logger.error(f"Описание ошибки: {result.get('description', 'N/A')}") logger.error(f"Код ошибки: {result.get('error_code', 'N/A')}") return False else: logger.error(f"HTTP ошибка: {response.status_code}") logger.error(f"Текст ответа: {response.text}") return False except httpx.TimeoutException as e: logger.error("=" * 80) logger.error("ТАЙМАУТ ПРИ ОТПРАВКЕ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") logger.error(f"Параметры запроса:") logger.error(f" chat_id: {CHANNEL_ID}") logger.error(f" text_length: {len(final_text)}") logger.error(f" parse_mode: HTML") return False except httpx.HTTPError as e: logger.error("=" * 80) logger.error("ОШИБКА HTTP ПРИ ОТПРАВКЕ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") return False except Exception as e: logger.error("=" * 80) logger.error("НЕОЖИДАННАЯ ОШИБКА ПРИ ОТПРАВКЕ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Все атрибуты ошибки: {vars(e) if hasattr(e, '__dict__') else 'N/A'}") logger.error(f"Полное представление: {repr(e)}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") return False finally: # Закрываем httpx клиент if httpx_client is not None: try: logger.info("Закрытие httpx клиента...") await httpx_client.aclose() logger.info("httpx клиент успешно закрыт") except Exception as e: logger.error(f"Ошибка при закрытии httpx клиента: {e}") logger.error(f" Тип ошибки: {type(e).__name__}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") async def main(): """Главная функция""" try: success = await test_telegram_publish() if success: logger.info("=" * 80) logger.info("ТЕСТ ЗАВЕРШЕН УСПЕШНО") logger.info("=" * 80) sys.exit(0) else: logger.error("=" * 80) logger.error("ТЕСТ ЗАВЕРШЕН С ОШИБКОЙ") logger.error("=" * 80) sys.exit(1) except KeyboardInterrupt: logger.warning("Прервано пользователем") sys.exit(1) except Exception as e: logger.error(f"Критическая ошибка: {e}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") sys.exit(1) if __name__ == "__main__": asyncio.run(main())