It's version 0.4
This commit is contained in:
@@ -12,7 +12,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class ScheduleService:
|
||||
def __init__(self):
|
||||
self.base_url = "https://college.by/accounts/raspis/{mouth:02d}/{day:02d}-PODNAM.htm"
|
||||
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 для расписания"""
|
||||
@@ -24,7 +26,11 @@ class ScheduleService:
|
||||
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)
|
||||
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
|
||||
@@ -39,11 +45,13 @@ class ScheduleService:
|
||||
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'
|
||||
"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 aiohttp.ClientSession(
|
||||
connector=connector, headers=headers
|
||||
) as session:
|
||||
async with session.get(url) as resp:
|
||||
raw_bytes = await resp.read()
|
||||
|
||||
@@ -71,24 +79,24 @@ class ScheduleService:
|
||||
else:
|
||||
result = f"📅 Расписание для {day} числа:\n```\n"
|
||||
for line in schedule_lines:
|
||||
formatted = (
|
||||
line.replace("¦", "│")
|
||||
.replace(" ", " ")
|
||||
.strip()
|
||||
)
|
||||
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]:
|
||||
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))
|
||||
context = await browser.new_context(
|
||||
viewport=ViewportSize(width=400, height=3000)
|
||||
)
|
||||
page = await context.new_page()
|
||||
|
||||
try:
|
||||
@@ -108,7 +116,7 @@ class ScheduleService:
|
||||
x=float(max(box["x"] - 0, 0)),
|
||||
y=float(max(box["y"] - 0, 0)),
|
||||
width=float(box["width"] + 150),
|
||||
height=float(box["height"] + 100)
|
||||
height=float(box["height"] + 100),
|
||||
)
|
||||
return await page.screenshot(clip=clip_rect), url, day, month
|
||||
|
||||
@@ -118,4 +126,4 @@ class ScheduleService:
|
||||
await context.close()
|
||||
await browser.close()
|
||||
|
||||
return None, url, day, month
|
||||
return None, url, day, month
|
||||
|
||||
+37
-20
@@ -44,10 +44,19 @@ class WatcherService:
|
||||
"""Основной цикл слежки"""
|
||||
while self.state.watcher_work:
|
||||
try:
|
||||
await self._check_all_groups()
|
||||
delay = randint(Config.WATCHER_BASE_DELAY, Config.WATCHER_BASE_DELAY + 100)
|
||||
logger.info(f"Следущая проверка через {delay}")
|
||||
await asyncio.sleep(delay)
|
||||
find = await self._check_all_groups()
|
||||
if find:
|
||||
# ничего не нашли → ждём
|
||||
delay = randint(
|
||||
Config.WATCHER_BASE_DELAY, Config.WATCHER_BASE_DELAY + 100
|
||||
)
|
||||
logger.info(f"Следующая проверка через {delay}")
|
||||
await asyncio.sleep(delay)
|
||||
else:
|
||||
# нашли → останавливаемся
|
||||
logger.info("Расписание найдено, останавливаем watcher")
|
||||
self.state.watcher_work = False
|
||||
break
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
@@ -63,33 +72,41 @@ class WatcherService:
|
||||
target += timedelta(days=1)
|
||||
return target
|
||||
|
||||
|
||||
async def _check_all_groups(self):
|
||||
"""Проверка всех групп на изменения"""
|
||||
async def _check_all_groups(self) -> bool:
|
||||
"""
|
||||
Возвращает True, если НИ в одной группе не найдено расписание.
|
||||
Возвращает False, если хотя бы в одной группе найдено расписание.
|
||||
"""
|
||||
day = self._get_target_day()
|
||||
found_any = False
|
||||
|
||||
for group, chat_id in Config.GROUP_CHATS.items():
|
||||
logger.info(f"Проверяем расписание для {group} на {day.strftime('%d.%m.%Y')}")
|
||||
await self._check_group_schedule(group, chat_id, day.day)
|
||||
logger.info(
|
||||
f"Проверяем расписание для {group} на {day.strftime('%d.%m.%Y')}"
|
||||
)
|
||||
found = await self._check_group_schedule(group, chat_id, day.day)
|
||||
if found:
|
||||
found_any = True
|
||||
|
||||
async def _check_group_schedule(self, group: str, chat_id: int, day: int):
|
||||
"""Проверка расписания для конкретной группы"""
|
||||
text, url, data_day, data_month = await self.schedule_service.get_schedule(group, day)
|
||||
return not found_any # <-- вот так правильно
|
||||
|
||||
async def _check_group_schedule(self, group: str, chat_id: int, day: int) -> bool:
|
||||
text, url, data_day, data_month = await self.schedule_service.get_schedule(
|
||||
group, day
|
||||
)
|
||||
if text and "не найдено" not in text.lower():
|
||||
msg = await self.bot.send_message(
|
||||
chat_id,
|
||||
f"Авто-расписание для {group} на {data_day:02d}.{data_month:02d}\n\n{text}",
|
||||
parse_mode="Markdown"
|
||||
parse_mode="Markdown",
|
||||
)
|
||||
await self.bot.pin_chat_message(chat_id, msg.message_id, disable_notification=True)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Не удалось получить расписание для {group}, {data_day}, {data_month}, {url}"
|
||||
await self.bot.pin_chat_message(
|
||||
chat_id, msg.message_id, disable_notification=False
|
||||
)
|
||||
return
|
||||
return True
|
||||
return False
|
||||
|
||||
#clip_hash = hashlib.md5(clip_png).hexdigest()
|
||||
# clip_hash = hashlib.md5(clip_png).hexdigest()
|
||||
|
||||
# Логика проверки изменений и отправки сообщений
|
||||
# ... (ваша существующая логика)
|
||||
# ... (ваша существующая логика)
|
||||
|
||||
Reference in New Issue
Block a user