Добавлено автоматическое обновление 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://<ip_хоста_с_vk_сервисом>: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` в корне проекта
3. Формат: Netscape cookies file
**Примечание:** Без cookies Instagram может блокировать запросы.
**Автоматическое обновление cookies:**
- Бот автоматически проверяет срок действия cookies каждые 24 часа
- Если cookies истекают через 3 дня (настраивается через `INSTAGRAM_AUTO_UPDATE_DAYS`), бот попытается автоматически обновить их из браузера
- Поддерживаются браузеры: Chrome, Firefox, Edge, Opera (по приоритету)
- Для автоматического обновления браузер должен быть установлен и доступен
**Примечание:** Без cookies Instagram может блокировать запросы. При первом запуске или если автоматическое обновление не сработало, обновите cookies вручную.
### 4. Запуск основного бота

223
bot.py
View file

@ -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)