Добавлено автоматическое обновление Instagram cookies и поддержание сессии

- Автоматическая проверка срока действия 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
This commit is contained in:
vrubelroman 2025-12-10 17:02:01 +03:00
parent ab82f94032
commit aa5c4cd1e6
3 changed files with 233 additions and 1 deletions

View file

@ -8,3 +8,6 @@ TELEGRAM_BOT_USERNAME=vrubelVideoDownload_bot
# Для локальной разработки: http://localhost:5555 # Для локальной разработки: http://localhost:5555
# Для продакшена: http://<ip_хоста_с_vk_сервисом>:5555 # Для продакшена: http://<ip_хоста_с_vk_сервисом>:5555
VK_DOWNLOADER_URL=http://localhost:5555 VK_DOWNLOADER_URL=http://localhost:5555
# Количество дней до истечения cookies, когда начинать автоматическое обновление (по умолчанию: 3)
INSTAGRAM_AUTO_UPDATE_DAYS=3

View file

@ -54,7 +54,13 @@ VK_DOWNLOADER_URL=http://localhost:5555
2. Сохраните файл как `instagram_cookies.txt` в корне проекта 2. Сохраните файл как `instagram_cookies.txt` в корне проекта
3. Формат: Netscape cookies file 3. Формат: Netscape cookies file
**Примечание:** Без cookies Instagram может блокировать запросы. **Автоматическое обновление cookies:**
- Бот автоматически проверяет срок действия cookies каждые 24 часа
- Если cookies истекают через 3 дня (настраивается через `INSTAGRAM_AUTO_UPDATE_DAYS`), бот попытается автоматически обновить их из браузера
- Поддерживаются браузеры: Chrome, Firefox, Edge, Opera (по приоритету)
- Для автоматического обновления браузер должен быть установлен и доступен
**Примечание:** Без cookies Instagram может блокировать запросы. При первом запуске или если автоматическое обновление не сработало, обновите cookies вручную.
### 4. Запуск основного бота ### 4. Запуск основного бота

223
bot.py
View file

@ -4,6 +4,8 @@ import json
import logging import logging
import asyncio import asyncio
import sqlite3 import sqlite3
import time
import subprocess
from pathlib import Path from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
from datetime import datetime 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 = os.getenv('INSTAGRAM_COOKIES_FILE', 'instagram_cookies.txt')
cookies_file_path = Path(cookies_file) 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) # Парсим cookies для получения csrf token (формат Netscape)
csrf_token = None csrf_token = None
sessionid = 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.") 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: async def download_vk_video(url: str, chat_id: int, max_retries: int = 3) -> str:
"""Скачивает видео с VK через внешний сервис""" """Скачивает видео с VK через внешний сервис"""
logger.info(f"VK: отправка запроса на внешний сервис {VK_DOWNLOADER_URL}") logger.info(f"VK: отправка запроса на внешний сервис {VK_DOWNLOADER_URL}")
@ -525,6 +739,15 @@ def main():
application.add_handler(CommandHandler("start", start_command)) application.add_handler(CommandHandler("start", start_command))
application.add_handler(CommandHandler("stat", stat_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("Бот запущен") logger.info("Бот запущен")
application.run_polling(allowed_updates=Update.ALL_TYPES) application.run_polling(allowed_updates=Update.ALL_TYPES)