diff --git a/dtek_notif/main.py b/dtek_notif/main.py index 6637257..3896473 100644 --- a/dtek_notif/main.py +++ b/dtek_notif/main.py @@ -1,10 +1,3 @@ -# Раздел "ТВАРИ, ЧИТАТЕ ВНИМАТЕЛЬНО!" - -# Когда обновят сайт -- расскоментровать 641-646 и закоментировать 647. -# Поменять комменты 671 и 672 местами. - - - import requests import json import asyncio @@ -89,7 +82,7 @@ def get_day_statistics(day_blocks: List[Dict]) -> Dict[str, int]: # ============================================================================ def parse_html(html: str) -> List[Dict]: - """Парсит HTML с графиком отключений (нова логіка)""" + """Парсит HTML с графиком отключений (логика от 15.11.2024)""" soup = BeautifulSoup(html, "html.parser") cells = soup.select(".disconnection-detailed-table-cell.cell") @@ -103,56 +96,71 @@ def parse_html(html: str) -> List[Dict]: cell_classes = cell.get('class', []) - # Перевіряємо чи вся година вимкнена + # ПРоверка статуса отключения на весь час full_hour_off = 'has_disconnection' in cell_classes and 'full_hour' in cell_classes - no_disconnection = 'no_disconnection' in cell_classes + no_disconnection_cell = 'no_disconnection' in cell_classes hour_block = cell.select_one(".hour_block") if not hour_block: continue - # Визначаємо статус та підтвердження для всієї години - confirmed = None + # Проверка подтверждённости отключения для всего часа + cell_confirmed = None if 'confirm_1' in cell_classes: - confirmed = True + cell_confirmed = True elif 'confirm_0' in cell_classes: - confirmed = False + cell_confirmed = False - # Шукаємо часткові відключення через div.fill - fill_divs = hour_block.select(".fill") + # Проверка половин часа + left = hour_block.select_one(".half.left") + right = hour_block.select_one(".half.right") - first_half_data = {"status": "on", "queue": None, "confirmed": None} - second_half_data = {"status": "on", "queue": None, "confirmed": None} + def parse_half(half, is_full_hour_off: bool) -> Dict: + """Парсит половину часа""" + if not half: + return {"status": "on", "queue": None, "confirmed": None} + + half_classes = half.get('class', []) + + # Если вся ячейка full_hour - используем статус ячейки + if is_full_hour_off: + return {"status": "off", "queue": None, "confirmed": cell_confirmed} + + # Определяем статус половины + if 'has_disconnection' in half_classes: + status = "off" + elif 'no_disconnection' in half_classes: + status = "on" + else: + status = "on" # По умолчанию считаем включенным + + # Если выключено - ищем подробности + queue = None + confirmed = None + + if status == "off": + disconnection_div = half.select_one(".disconnection") + if disconnection_div: + # Ищем номер черги в title + if disconnection_div.has_attr("title"): + title = disconnection_div["title"] + if "Номер черги" in title or "Номер черги:" in title: + try: + queue = title.split(":")[-1].strip() + except: + pass + + # Определяем подтверждение + disc_classes = disconnection_div.get('class', []) + if 'disconnection_confirm_1' in disc_classes: + confirmed = True + elif 'disconnection_confirm_0' in disc_classes: + confirmed = False + + return {"status": status, "queue": queue, "confirmed": confirmed} - if full_hour_off: - # Вся година вимкнена - first_half_data = {"status": "off", "queue": None, "confirmed": confirmed} - second_half_data = {"status": "off", "queue": None, "confirmed": confirmed} - elif no_disconnection and not fill_divs: - # Вся година ввімкнена - first_half_data = {"status": "on", "queue": None, "confirmed": None} - second_half_data = {"status": "on", "queue": None, "confirmed": None} - else: - # Перевіряємо часткові відключення - for fill_div in fill_divs: - title = fill_div.get('title', '') - style = fill_div.get('style', '') - fill_classes = fill_div.get('class', []) - - # Визначаємо підтвердження - fill_confirmed = None - if 'confirmed' in fill_classes: - fill_confirmed = True - elif 'possible' in fill_classes: - fill_confirmed = False - - # Парсимо стиль для визначення часу - # --start:0;--size:50 означає 0-30 хвилин (перша половина) - # --start:50;--size:50 означає 30-60 хвилин (друга половина) - if '--start:0' in style or title.endswith(':00—' + f'{current_hour:02d}:30'): - first_half_data = {"status": "off", "queue": None, "confirmed": fill_confirmed} - elif '--start:50' in style or (f'{current_hour:02d}:30' in title and title.endswith(':00')): - second_half_data = {"status": "off", "queue": None, "confirmed": fill_confirmed} + first_half_data = parse_half(left, full_hour_off) + second_half_data = parse_half(right, full_hour_off) schedule.append({ "hour": current_hour, @@ -168,6 +176,7 @@ def parse_html(html: str) -> List[Dict]: return schedule + def get_voe_html(city_id: int, street_id: int, house_id: int) -> str: """Получает HTML с сайта VOE""" url = "https://www.voe.com.ua/disconnection/detailed?ajax_form=1&_wrapper_format=drupal_ajax" @@ -582,7 +591,12 @@ async def cmd_info(message: Message): await message.answer( "ℹ️ Про бота\n\n" - "🚀 Версія: 2.1 (Оптимізована)\n" + "🚀 Версія: 2.2 (Стабільна)\n\n" + "📝 Реліз-ноути:\n" + "├ 15.11.2024: Адаптація під оновлену логіку сайту VOE\n" + "├ Виправлено парсинг half.left та half.right\n" + "├ Покращено визначення підтвердження відключень\n" + "└ Оптимізовано обробку статусу для всієї години\n\n" "⚡ Функціональність:\n" "├ Моніторинг графіку 24/7\n" "├ Сповіщення за 5 хвилин\n" @@ -645,21 +659,18 @@ async def cmd_tomorrow(message: Message): return try: - # """Закоментировал до лучших времен""" - # await message.answer("⏳ Завантаження графіку завтра...") - # html = get_voe_html(VOE_CITY_ID, VOE_STREET_ID, VOE_HOUSE_ID) - # schedule = parse_html(html) - # text = format_single_day_schedule(schedule, 1) - # await message.answer(text, parse_mode="HTML", reply_markup=get_main_keyboard()) - await message.answer("❌ Функція тимчасово недоступна. Чекаємо на оновлення сайту", parse_mode="HTML", reply_markup=get_main_keyboard()) + await message.answer("⏳ Завантаження графіку завтра...") + html = get_voe_html(VOE_CITY_ID, VOE_STREET_ID, VOE_HOUSE_ID) + schedule = parse_html(html) + text = format_single_day_schedule(schedule, 1) + await message.answer(text, parse_mode="HTML", reply_markup=get_main_keyboard()) + # await message.answer("❌ Функція тимчасово недоступна. Чекаємо на оновлення сайту", parse_mode="HTML", reply_markup=get_main_keyboard()) except Exception as e: await message.answer( f"❌ Помилка: {str(e)}", parse_mode="HTML", reply_markup=get_main_keyboard() ) -# Когда обновят сайт -- расскоментровать 641-646 и закоментировать 647. -# Поменять комменты 671 и 672 местами. @dp.message(Command("check")) @@ -675,8 +686,7 @@ async def cmd_check(message: Message): new_schedule = parse_html(html) prefix = "✅ Знайдено зміни!\n\n" if schedules_differ(last_schedule, new_schedule) else "✓ Графік без змін\n\n" - # result = prefix + format_schedule_message(new_schedule, days_to_show=2) - result = prefix + format_schedule_message(new_schedule, days_to_show=1) + result = prefix + format_schedule_message(new_schedule, days_to_show=2) await message.answer(result, parse_mode="HTML", reply_markup=get_main_keyboard()) except Exception as e: @@ -703,9 +713,9 @@ async def handle_text(message: Message): elif text == "📅 Сьогодні": await cmd_today(message) - # # Кнопка "Завтра" - # elif text == "📅 Завтра": - # await cmd_tomorrow(message) + # Кнопка "Завтра" + elif text == "📅 Завтра": + await cmd_tomorrow(message) # Кнопка "Оновити" elif text == "🔄 Оновити": @@ -733,7 +743,7 @@ async def handle_text(message: Message): async def main(): """Главная функция""" logger.info("=" * 50) - logger.info("🚀 ЗАПУСК БОТА V2.1 (ОПТИМІЗОВАНА ВЕРСІЯ)") + logger.info("ЗАПУСК БОТА V2.2 (stable 2.2, 15.11.2025)") logger.info("=" * 50) if not TELEGRAM_TOKEN or TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN", "YOUR_TOKEN_HERE") @@ -744,7 +754,7 @@ async def main(): logger.error("❌ ALLOWED_CHAT_IDS не конфігуровані! Напишіть ID в .env файл") return - logger.info(f"🔌 Дозволені чати: {ALLOWED_CHAT_IDS}") + logger.info(f"📌 Allowed chat ids: {ALLOWED_CHAT_IDS}") logger.info(f"⏱ Інтервал перевірки: {CHECK_INTERVAL} сек") logger.info("=" * 50)