Files
instaloader_1/bot.py
smolkik-code 6c51413d12 feat: Telegram bot for Instagram monitoring via instaloader
- aiogram 3 bot with commands /start /check /status /stop
- InstagramMonitor: periodic check of followees stories and posts
- Docker deployment with docker-compose
- State tracking to avoid duplicate sends
- Auto-cleanup of old media files
2026-06-17 11:30:13 +07:00

134 lines
4.4 KiB
Python

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
from config import config
from instagram_monitor import InstagramMonitor
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:
state_file = Path(config.download_dir) / "state.json"
tracked = 0
if state_file.exists():
import json
with open(state_file) as f:
tracked = len(json.load(f))
await message.answer(
f"Отслежено медиа: {tracked}\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[Файл не удалось отправить]")
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())