import os import logging import httpx import asyncio import time import sys from datetime import datetime from telegram import Bot from telegram.request import HTTPXRequest from telegram.error import TelegramError, RetryAfter, TimedOut from dotenv import load_dotenv # Загрузка переменных окружения 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}") # Проверка обязательных параметров 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'🔎 Инфо | 📌 Иду | 🔜 Подписка' 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 клиент и бота с отключенным HTTP/2 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") # Создаем HTTPXRequest с явным указанием HTTP/1.1 (отключаем HTTP/2) # HTTPXRequest автоматически создаст httpx клиент с http2=False при http_version="1.1" logger.info("Создание HTTPXRequest с http_version='1.1' (HTTP/2 отключен)...") request = HTTPXRequest(http_version="1.1", connection_pool_size=8) logger.info("HTTPXRequest создан с http_version='1.1'") # Примечание: HTTPXRequest создаст свой собственный httpx клиент с http2=False # Наш httpx_client будет закрыт в finally, но не используется Bot'ом # Это нормально - главное, что HTTP/2 отключен через http_version="1.1" # Создаем бота с отключенным HTTP/2 logger.info("Создание объекта Bot с отключенным HTTP/2...") bot = Bot(token=POSTER_BOT_TOKEN, request=request) logger.info("Объект Bot успешно создан с отключенным HTTP/2") # Получаем информацию о боте try: logger.info("Попытка получения информации о боте через bot.get_me()...") logger.debug(f"Параметры запроса get_me(): нет параметров (метод без аргументов)") bot_info = await bot.get_me() logger.info(f"Информация о боте:") logger.info(f" ID: {bot_info.id}") logger.info(f" Username: @{bot_info.username}") logger.info(f" First name: {bot_info.first_name}") logger.info(f" Is bot: {bot_info.is_bot}") except TimedOut 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" Метод: bot.get_me()") logger.error(f" Токен: {'*' * 20 if POSTER_BOT_TOKEN else 'НЕ ЗАДАН'}") logger.error(f" Длина токена: {len(POSTER_BOT_TOKEN) if POSTER_BOT_TOKEN else 0}") except RetryAfter as e: logger.error("=" * 80) logger.error("ОШИБКА FLOODWAIT (429) при получении информации о боте!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Необходимо подождать: {e.retry_after} секунд") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") except TelegramError as e: logger.error("=" * 80) logger.error("ОШИБКА TELEGRAM при получении информации о боте!") 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" Метод: bot.get_me()") logger.error(f" Токен: {'*' * 20 if POSTER_BOT_TOKEN else 'НЕ ЗАДАН'}") logger.error(f" Длина токена: {len(POSTER_BOT_TOKEN) if POSTER_BOT_TOKEN else 0}") logger.error(f" Первые 10 символов токена: {POSTER_BOT_TOKEN[:10] if POSTER_BOT_TOKEN and len(POSTER_BOT_TOKEN) >= 10 else 'N/A'}") 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)}") logger.error(f"Параметры запроса:") logger.error(f" Метод: bot.get_me()") logger.error(f" Токен: {'*' * 20 if POSTER_BOT_TOKEN else 'НЕ ЗАДАН'}") logger.error(f" Длина токена: {len(POSTER_BOT_TOKEN) if POSTER_BOT_TOKEN else 0}") import traceback logger.error(f"Трассировка стека:\n{traceback.format_exc()}") # Публикуем тестовое сообщение try: logger.info("=" * 80) logger.info("ПОПЫТКА ОТПРАВКИ ТЕСТОВОГО СООБЩЕНИЯ") logger.info("=" * 80) 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") start_time = datetime.now() message = await bot.send_message( chat_id=CHANNEL_ID, text=final_text, parse_mode="HTML", disable_notification=PUBLISH_SILENTLY, disable_web_page_preview=True ) end_time = datetime.now() duration = (end_time - start_time).total_seconds() logger.info("=" * 80) logger.info("СООБЩЕНИЕ УСПЕШНО ОТПРАВЛЕНО!") logger.info("=" * 80) logger.info(f"Message ID: {message.message_id}") logger.info(f"Chat ID: {message.chat.id}") logger.info(f"Chat type: {message.chat.type}") logger.info(f"Chat title: {message.chat.title if hasattr(message.chat, 'title') else 'N/A'}") logger.info(f"Время отправки: {duration:.2f} секунд") logger.info(f"Дата сообщения: {message.date}") logger.info("=" * 80) return True except TimedOut 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 RetryAfter as e: logger.error("=" * 80) logger.error("ОШИБКА FLOODWAIT (429) ПРИ ОТПРАВКЕ СООБЩЕНИЯ!") logger.error("=" * 80) logger.error(f"Тип ошибки: {type(e).__name__}") logger.error(f"Сообщение: {str(e)}") logger.error(f"Необходимо подождать: {e.retry_after} секунд") logger.error(f"Все атрибуты ошибки: {vars(e)}") logger.error(f"Полное представление: {repr(e)}") return False except TelegramError as e: logger.error("=" * 80) logger.error("ОШИБКА TELEGRAM ПРИ ОТПРАВКЕ СООБЩЕНИЯ!") 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 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())