It's my tg bot for schedule. version 0.1

This commit is contained in:
Niken
2025-10-04 16:59:38 +03:00
commit 58b47bec5e
16 changed files with 767 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import hashlib
from datetime import datetime, timedelta
from typing import Optional, Tuple
from playwright.async_api import async_playwright, ViewportSize, FloatRect
import logging
logger = logging.getLogger(__name__)
class ScheduleService:
def __init__(self):
self.base_url = "https://college.by/accounts/raspis/{mouth:02d}/{day:02d}-PODNAM.htm"
def _make_url(self, day: int = 0) -> Tuple[str, int, int]:
"""Генерация URL для расписания"""
d = datetime.now()
if day == 0:
if d.hour >= 12:
d += timedelta(days=1)
if d.weekday() == 6:
d += timedelta(days=1)
return self.base_url.format(day=d.day, mouth=d.month), d.day, d.month
else:
return self.base_url.format(day=int(day), mouth=d.month), int(day), int(d.month)
async def get_schedule(self, group: str, day_offset: int = 0) -> Tuple[Optional[bytes], str, int, int]:
"""Получение скриншота расписания"""
url, day, month = self._make_url(day_offset)
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(viewport=ViewportSize(width=400, height=3000))
page = await context.new_page()
try:
response = await page.goto(url, wait_until="networkidle", timeout=30000)
if not response or response.status != 200:
logger.warning(f"Ошибка загрузки страницы: {url}")
return None, url, day, month
locator = page.locator("p", has_text=group).first
if await locator.count() > 0:
await locator.scroll_into_view_if_needed()
box = await locator.bounding_box()
if box:
clip_rect = FloatRect(
x=float(max(box["x"] - 0, 0)),
y=float(max(box["y"] - 0, 0)),
width=float(box["width"] + 150),
height=float(box["height"] + 100)
)
return await page.screenshot(clip=clip_rect), url, day, month
except Exception as e:
logger.error(f"Ошибка при получении расписания: {e}")
finally:
await context.close()
await browser.close()
return None, url, day, month
+97
View File
@@ -0,0 +1,97 @@
import asyncio
import hashlib
from datetime import datetime, timedelta
from random import randint
from aiogram import Bot
from aiogram.types import BufferedInputFile
from models.state import BotState
from config import Config
from services.schedule_service import ScheduleService
import logging
logger = logging.getLogger(__name__)
class WatcherService:
def __init__(self, state: BotState, bot: Bot):
self.state = state
self.bot = bot
self.schedule_service = ScheduleService()
async def start(self):
"""Запуск слежки"""
if self.state.watcher_work:
return
self.state.watcher_work = True
self.state.watcher_task = asyncio.create_task(self._watcher_loop())
logger.info("Watcher запущен")
async def stop(self):
"""Остановка слежки"""
if not self.state.watcher_work:
return
self.state.watcher_work = False
if self.state.watcher_task:
self.state.watcher_task.cancel()
try:
await self.state.watcher_task
except asyncio.CancelledError:
pass
logger.info("Watcher остановлен")
async def _watcher_loop(self):
"""Основной цикл слежки"""
while self.state.watcher_work:
try:
await self._check_all_groups()
delay = randint(600, 700)
await asyncio.sleep(delay)
except asyncio.CancelledError:
break
except Exception as e:
logger.error(f"Ошибка в watcher_loop: {e}")
await asyncio.sleep(60)
def _get_target_day(self) -> datetime:
"""Получение целевого дня"""
now = datetime.now()
target = now + timedelta(days=1)
if target.weekday() == 6:
target += timedelta(days=1)
return target
async def _check_all_groups(self):
"""Проверка всех групп на изменения"""
day = self._get_target_day()
for group, chat_id in Config.GROUP_CHATS.items():
await self._check_group_schedule(group, chat_id, day)
async def _check_group_schedule(self, group: str, chat_id: int, day: int):
"""Проверка расписания для конкретной группы"""
logger.info(f"Проверяем расписание для {group} на {day.strftime('%d.%m.%Y')}")
clip_png, url, data_day, data_mouth = await self.schedule_service.get_schedule(group, day.day)
if clip_png:
msg = await self.bot.send_photo(
chat_id,
BufferedInputFile(clip_png, filename=f"{group}.png"),
caption=f"Авто-расписание для {group} на {data_day:02d}.{data_mouth:02d}"
)
await self.bot.pin_chat_message(chat_id, msg.message_id, disable_notification=True)
else:
logger.warning(f"Не удалось получить расписание для {group}, {data_day}, {data_mouth}, {url}")
return
#clip_hash = hashlib.md5(clip_png).hexdigest()
# Логика проверки изменений и отправки сообщений
# ... (ваша существующая логика)