import asyncio import os from pathlib import Path from aiogram import Bot, Dispatcher, F, Router from aiogram.filters import CommandStart from aiogram.types import FSInputFile, Message from loguru import logger import sys logger.remove() logger.add(sys.stderr, level="DEBUG") from config import config from instagram_monitor import InstagramMonitor, API_BASE router = Router() bot = Bot(token=config.telegram_bot_token) monitor = InstagramMonitor() @router.message(CommandStart()) async def cmd_start(message: Message) -> None: if message.from_user and message.from_user.id == config.telegram_chat_id: await message.answer( "Instagram Monitor Bot запущен.\n" f"Проверяю подписки каждые {config.check_interval_minutes} минут.\n" "Команды:\n" "/check — проверить сейчас\n" "/status — статус бота\n" "/stop — остановить мониторинг" ) else: await message.answer("Этот бот не для тебя.") @router.message(F.text == "/status") async def cmd_status(message: Message) -> None: if message.from_user and message.from_user.id == config.telegram_chat_id: stats = monitor.db.get_stats() stats_text = ", ".join(f"{k}: {v}" for k, v in stats.items()) if stats else "пусто" await message.answer( f"В базе: {stats_text}\n" f"Интервал: {config.check_interval_minutes} мин\n" f"Instagram: {config.instagram_username}" ) @router.message(F.text == "/stop") async def cmd_stop(message: Message) -> None: if message.from_user and message.from_user.id == config.telegram_chat_id: await message.answer("Останавливаю мониторинг...") import sys sys.exit(0) @router.message(F.text == "/check") async def cmd_check(message: Message) -> None: if message.from_user and message.from_user.id == config.telegram_chat_id: await message.answer("Проверяю обновления...") count = 0 async for update in monitor.check_updates(): await send_media(update) count += 1 await message.answer(f"Готово. Найдено: {count}") async def send_media(update: dict) -> None: chat_id = config.telegram_chat_id media_path = update["media_path"] username = update["username"] media_type = update["type"] caption = update["caption"] shortcode = update["shortcode"] header = f"{'Сторис' if media_type == 'story' else 'Пост'} от @{username}" if caption: text = f"{header}\n\n{caption}" else: text = header path = Path(media_path) if not path.exists(): logger.warning(f"Media file not found: {media_path}") return try: if path.suffix == ".mp4": video = FSInputFile(str(path)) await bot.send_video(chat_id=chat_id, video=video, caption=text, parse_mode=None) elif path.suffix in (".jpg", ".jpeg", ".png", ".webp"): photo = FSInputFile(str(path)) await bot.send_photo(chat_id=chat_id, photo=photo, caption=text, parse_mode=None) else: document = FSInputFile(str(path)) await bot.send_document(chat_id=chat_id, document=document, caption=text, parse_mode=None) except Exception as e: logger.error(f"Failed to send media: {e}") await bot.send_message(chat_id=chat_id, text=f"{header}\n\n{caption}\n\n[Файл не удалось отправить]") @router.message(F.text.startswith("/debug")) async def cmd_debug(message: Message) -> None: if message.from_user and message.from_user.id == config.telegram_chat_id: parts = message.text.split() if len(parts) < 2: await message.answer("Использование: /debug @username") return username = parts[1].lstrip("@") await message.answer(f"Ищу @{username}...") followees = monitor._get_followees() user = next((u for u in followees if u["username"] == username), None) if not user: await message.answer(f"@{username} не найден в подписках") return user_pk = user["pk"] await message.answer(f"user_pk: {user_pk}\nПроверяю сториз...") resp = monitor.session.get( f"{API_BASE}/feed/user/{user_pk}/story/", params={"user_id": user_pk}, timeout=30, ) await message.answer(f"HTTP {resp.status_code}\nResponse: {resp.text[:800]}") async def monitor_loop() -> None: logger.info("Starting monitor loop") while True: try: async for update in monitor.check_updates(): await send_media(update) monitor.cleanup_downloads(max_age_hours=48) except Exception as e: logger.error(f"Monitor loop error: {e}") await asyncio.sleep(config.check_interval_minutes * 60) async def main() -> None: errors = config.validate() if errors: for e in errors: logger.error(e) return dp = Dispatcher() dp.include_router(router) asyncio.create_task(monitor_loop()) logger.info("Bot started") await dp.start_polling(bot) if __name__ == "__main__": import asyncio asyncio.run(main())