From aa5c4cd1e6dc109e7fe8d599a301e5842bc7368b Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Wed, 10 Dec 2025 17:02:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Instagram=20cookies=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Автоматическая проверка срока действия cookies каждые 24 часа - Автоматическое обновление cookies за 3 дня до истечения из браузера - Поддержание активности сессии через периодические запросы - Поддержка Chrome, Firefox, Edge, Opera для обновления cookies - Добавлена функция update_instagram_cookies_from_browser() - Добавлена функция check_instagram_cookies_expiry() - Фоновая задача keep_instagram_session_alive() для поддержания сессии - Обновлена документация в README.md - Добавлена переменная INSTAGRAM_AUTO_UPDATE_DAYS в .env.example --- .env.example | 3 + README.md | 8 +- bot.py | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 4dd775f..68481d3 100644 --- a/.env.example +++ b/.env.example @@ -8,3 +8,6 @@ TELEGRAM_BOT_USERNAME=vrubelVideoDownload_bot # Для локальной разработки: http://localhost:5555 # Для продакшена: http://:5555 VK_DOWNLOADER_URL=http://localhost:5555 + +# Количество дней до истечения cookies, когда начинать автоматическое обновление (по умолчанию: 3) +INSTAGRAM_AUTO_UPDATE_DAYS=3 diff --git a/README.md b/README.md index 91e351b..b2b078c 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,13 @@ VK_DOWNLOADER_URL=http://localhost:5555 2. Сохраните файл как `instagram_cookies.txt` в корне проекта 3. Формат: Netscape cookies file -**Примечание:** Без cookies Instagram может блокировать запросы. +**Автоматическое обновление cookies:** +- Бот автоматически проверяет срок действия cookies каждые 24 часа +- Если cookies истекают через 3 дня (настраивается через `INSTAGRAM_AUTO_UPDATE_DAYS`), бот попытается автоматически обновить их из браузера +- Поддерживаются браузеры: Chrome, Firefox, Edge, Opera (по приоритету) +- Для автоматического обновления браузер должен быть установлен и доступен + +**Примечание:** Без cookies Instagram может блокировать запросы. При первом запуске или если автоматическое обновление не сработало, обновите cookies вручную. ### 4. Запуск основного бота diff --git a/bot.py b/bot.py index 7d821b7..87c40ea 100644 --- a/bot.py +++ b/bot.py @@ -4,6 +4,8 @@ import json import logging import asyncio import sqlite3 +import time +import subprocess from pathlib import Path from urllib.parse import urlparse from datetime import datetime @@ -248,6 +250,15 @@ async def download_instagram_video(url: str, chat_id: int, max_retries: int = 3) cookies_file = os.getenv('INSTAGRAM_COOKIES_FILE', 'instagram_cookies.txt') cookies_file_path = Path(cookies_file) + # Проверяем срок действия cookies перед использованием + if cookies_file_path.exists(): + is_valid, days_left = check_instagram_cookies_expiry() + if not is_valid: + logger.error("Instagram cookies истекли! Необходимо обновить cookies.") + raise Exception("Instagram cookies истекли. Пожалуйста, обновите cookies в файле instagram_cookies.txt") + elif days_left < 7: + logger.warning(f"Instagram cookies истекают через {days_left} дней. Рекомендуется обновить.") + # Парсим cookies для получения csrf token (формат Netscape) csrf_token = None sessionid = None @@ -329,6 +340,209 @@ async def download_instagram_video(url: str, chat_id: int, max_retries: int = 3) raise last_error or Exception("Неизвестная ошибка при скачивании с Instagram. Возможно, нужно обновить cookies.") +async def update_instagram_cookies_from_browser(browser: str = 'chrome') -> bool: + """ + Автоматически обновляет Instagram cookies из браузера + Returns: True если успешно, False если ошибка + """ + cookies_file = os.getenv('INSTAGRAM_COOKIES_FILE', 'instagram_cookies.txt') + cookies_file_path = Path(cookies_file) + + try: + logger.info(f"Попытка обновления Instagram cookies из браузера {browser}...") + + # Пробуем обновить cookies из браузера + loop = asyncio.get_event_loop() + result = await loop.run_in_executor( + None, + lambda: subprocess.run( + [ + 'yt-dlp', + '--cookies-from-browser', browser, + '--cookies', str(cookies_file_path.absolute()), + '--no-download', + 'https://www.instagram.com/' + ], + capture_output=True, + timeout=30, + text=True + ) + ) + + if result.returncode == 0: + logger.info(f"✅ Instagram cookies успешно обновлены из браузера {browser}") + return True + else: + logger.warning(f"Не удалось обновить cookies из {browser}: {result.stderr[:200]}") + return False + + except subprocess.TimeoutExpired: + logger.warning(f"Таймаут при обновлении cookies из {browser}") + return False + except FileNotFoundError: + logger.warning(f"yt-dlp не найден для обновления cookies") + return False + except Exception as e: + logger.error(f"Ошибка при обновлении cookies из браузера: {e}") + return False + + +def check_instagram_cookies_expiry() -> tuple[bool, int]: + """ + Проверяет срок действия Instagram cookies + Returns: (is_valid, days_until_expiry) + """ + cookies_file = os.getenv('INSTAGRAM_COOKIES_FILE', 'instagram_cookies.txt') + cookies_file_path = Path(cookies_file) + + if not cookies_file_path.exists(): + return False, 0 + + try: + current_time = time.time() + min_expiry = None + + with open(cookies_file_path, 'r') as f: + for line in f: + line = line.strip() + if not line or line.startswith('#'): + continue + parts = line.split('\t') + if len(parts) >= 7: + domain = parts[0] + if 'instagram' in domain.lower(): + try: + expiry = int(parts[4]) # Unix timestamp + if min_expiry is None or expiry < min_expiry: + min_expiry = expiry + except (ValueError, IndexError): + continue + + if min_expiry is None: + return False, 0 + + days_until_expiry = (min_expiry - current_time) / 86400 + is_valid = min_expiry > current_time + + return is_valid, int(days_until_expiry) + except Exception as e: + logger.error(f"Ошибка при проверке срока действия cookies: {e}") + return False, 0 + + +async def keep_instagram_session_alive(): + """Поддерживает сессию Instagram активной через периодические запросы и автоматически обновляет cookies""" + cookies_file = os.getenv('INSTAGRAM_COOKIES_FILE', 'instagram_cookies.txt') + cookies_file_path = Path(cookies_file) + AUTO_UPDATE_DAYS_BEFORE_EXPIRY = int(os.getenv('INSTAGRAM_AUTO_UPDATE_DAYS', '3')) # Обновлять за 3 дня до истечения + + if not cookies_file_path.exists(): + logger.info("Instagram cookies не найдены, пропускаем поддержание сессии") + return + + # Список браузеров для попытки обновления (по приоритету) + browsers_to_try = ['chrome', 'firefox', 'edge', 'opera'] + + # Проверяем cookies при старте + is_valid, days_left = check_instagram_cookies_expiry() + if not is_valid: + logger.warning("Instagram cookies истекли! Пытаемся автоматически обновить...") + # Пытаемся обновить из браузера + updated = False + for browser in browsers_to_try: + if await update_instagram_cookies_from_browser(browser): + updated = True + break + + if not updated: + logger.error("Не удалось автоматически обновить cookies! Необходимо обновить вручную.") + return + else: + # Перепроверяем после обновления + is_valid, days_left = check_instagram_cookies_expiry() + + if days_left < AUTO_UPDATE_DAYS_BEFORE_EXPIRY: + logger.warning(f"Instagram cookies истекают через {days_left} дней! Пытаемся автоматически обновить...") + # Пытаемся обновить заранее + for browser in browsers_to_try: + if await update_instagram_cookies_from_browser(browser): + # Перепроверяем + _, days_left = check_instagram_cookies_expiry() + logger.info(f"Cookies обновлены! Новый срок: {days_left} дней") + break + else: + logger.info(f"Instagram cookies действительны еще {days_left} дней") + + # Интервал проверки: 24 часа (86400 секунд) + check_interval = 86400 + + while True: + try: + await asyncio.sleep(check_interval) + + # Проверяем срок действия перед каждым запросом + is_valid, days_left = check_instagram_cookies_expiry() + + # Автоматическое обновление за N дней до истечения + if days_left < AUTO_UPDATE_DAYS_BEFORE_EXPIRY and days_left > 0: + logger.warning(f"Instagram cookies истекают через {days_left} дней. Автоматическое обновление...") + updated = False + for browser in browsers_to_try: + if await update_instagram_cookies_from_browser(browser): + updated = True + _, days_left = check_instagram_cookies_expiry() + logger.info(f"✅ Cookies обновлены автоматически! Новый срок: {days_left} дней") + break + + if not updated: + logger.warning("Не удалось автоматически обновить cookies. Попробуйте обновить вручную.") + + if not is_valid: + logger.error("Instagram cookies истекли! Пытаемся автоматически обновить...") + updated = False + for browser in browsers_to_try: + if await update_instagram_cookies_from_browser(browser): + updated = True + is_valid, days_left = check_instagram_cookies_expiry() + break + + if not updated: + logger.error("Не удалось автоматически обновить cookies! Остановка поддержания сессии.") + break + + # Делаем легкий запрос к Instagram для поддержания активности + logger.info("Поддерживаем активность сессии Instagram...") + try: + ydl_opts = { + 'cookiefile': str(cookies_file_path.absolute()), + 'quiet': True, + 'no_warnings': True, + 'socket_timeout': 10, + } + + loop = asyncio.get_event_loop() + await loop.run_in_executor( + None, + lambda: yt_dlp.YoutubeDL(ydl_opts).extract_info( + 'https://www.instagram.com/', + download=False + ) + ) + + logger.info(f"Сессия Instagram успешно обновлена. Cookies действительны еще {days_left} дней") + except Exception as e: + logger.warning(f"Не удалось обновить сессию Instagram: {e}") + # Продолжаем работу, попробуем в следующий раз + + except asyncio.CancelledError: + logger.info("Поддержание сессии Instagram остановлено") + break + except Exception as e: + logger.error(f"Ошибка в задаче поддержания сессии Instagram: {e}") + # Ждем перед следующей попыткой + await asyncio.sleep(3600) # 1 час + + async def download_vk_video(url: str, chat_id: int, max_retries: int = 3) -> str: """Скачивает видео с VK через внешний сервис""" logger.info(f"VK: отправка запроса на внешний сервис {VK_DOWNLOADER_URL}") @@ -525,6 +739,15 @@ def main(): application.add_handler(CommandHandler("start", start_command)) application.add_handler(CommandHandler("stat", stat_command)) + # Запускаем фоновую задачу для поддержания сессии Instagram + async def post_init(application: Application): + """Выполняется после инициализации приложения""" + # Запускаем задачу поддержания сессии Instagram в фоне + asyncio.create_task(keep_instagram_session_alive()) + logger.info("Фоновая задача поддержания сессии Instagram запущена") + + application.post_init = post_init + # Запускаем бота logger.info("Бот запущен") application.run_polling(allowed_updates=Update.ALL_TYPES)