Files
myfirstprogram/services/schedule_service.py
T
Niken 3ef1327b67 I add command /prasp
It's version 0.2.2
2025-10-05 22:29:49 +03:00

121 lines
4.7 KiB
Python

from datetime import datetime, timedelta
from typing import Optional, Tuple
from playwright.async_api import async_playwright, ViewportSize, FloatRect
import logging
import aiohttp
from bs4 import BeautifulSoup
import ssl
import certifi
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[str, str, int, int]:
"""Получение текста расписания (аналог Rust parse_schedule)"""
url, day, month = self._make_url(day_offset)
ssl_context = ssl.create_default_context(cafile=certifi.where())
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
connector = aiohttp.TCPConnector(ssl=ssl_context)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
}
# тут можно использовать aiohttp + chardet/charset_normalizer
async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
async with session.get(url) as resp:
raw_bytes = await resp.read()
decoded = raw_bytes.decode("cp1251", errors="ignore")
document = BeautifulSoup(decoded, "html.parser")
# ищем <p class="MsoPlainText"><b>...</b>
elements = document.select("p.MsoPlainText b")
found_group = False
schedule_lines = []
for el in elements:
text = el.get_text(strip=True)
if not found_group:
if group in text:
found_group = True
schedule_lines.append(text)
else:
if "-----" in text or "+----" in text:
break
schedule_lines.append(text)
if not schedule_lines:
result = f"Расписание для группы {group} на {day} число не найдено"
else:
result = f"📅 Расписание для {day} числа:\n```\n"
for line in schedule_lines:
formatted = (
line.replace("¦", "")
.replace(" ", " ")
.strip()
)
if formatted:
result += f"{formatted}\n"
result += "```"
return result, url, day, month
async def get_pschedule(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