From 7ca26b77a7fdac3d68aa06db3a7f99d108697dba Mon Sep 17 00:00:00 2001 From: mr-forust Date: Wed, 12 Nov 2025 18:23:53 +0100 Subject: [PATCH] Enhance schedule formatting and error handling in DTEK notification bot v2 --- dtek_notif/main.py | 224 ++++++++++++++++++++++++--------------------- 1 file changed, 121 insertions(+), 103 deletions(-) diff --git a/dtek_notif/main.py b/dtek_notif/main.py index 8c06d49..a1919ea 100644 --- a/dtek_notif/main.py +++ b/dtek_notif/main.py @@ -143,9 +143,9 @@ def get_main_keyboard(): def format_schedule_message(schedule, days_to_show=2): - """Форматирует расписание для отправки в Telegram (компактная версия)""" - message = f"⚡️ Графік відключень\n" - message += f"🕐 {datetime.now().strftime('%d.%m.%Y %H:%M')}\n\n" + """Форматирует расписание для отправки в Telegram""" + message = f"⚡️ Графік відключень світла\n" + message += f"🕐 Оновлено: {datetime.now().strftime('%d.%m.%Y %H:%M')}\n\n" start_date = datetime.now() @@ -155,138 +155,142 @@ def format_schedule_message(schedule, days_to_show=2): continue date_str = (start_date + timedelta(days=day)).strftime('%d.%m') - day_name = "Сьогодні" if day == 0 else "Завтра" + day_name = "🌅 Сьогодні" if day == 0 else "🌄 Завтра" + message += f"{'─' * 32}\n" message += f"📅 {day_name} ({date_str})\n" + message += f"{'─' * 32}\n\n" # Группируем последовательные отключения - disconnections = [] - i = 0 - while i < len(day_blocks): - block = day_blocks[i] - first = block["first_half"] - second = block["second_half"] - - # Проверяем статус отключения - first_off = first["status"] == "off" - second_off = second["status"] == "off" - - if first_off or second_off: - start_time = f"{block['hour']:02d}:00" if first_off else f"{block['hour']:02d}:30" - - # Ищем конец периода отключения - j = i - while j < len(day_blocks): - b = day_blocks[j] - f = b["first_half"] - s = b["second_half"] - - # Если обе половины не отключены, прерываем - if f["status"] != "off" and s["status"] != "off": - break - - j += 1 - - # Определяем конечное время - if j > 0: - prev_block = day_blocks[j-1] - if prev_block["second_half"]["status"] == "off": - end_time = f"{prev_block['hour']+1:02d}:00" - else: - end_time = f"{prev_block['hour']:02d}:30" - else: - end_time = f"{block['hour']+1:02d}:00" - - # Получаем информацию о черге и подтверждении - queue = first["queue"] or second["queue"] - confirmed = first["confirmed"] if first_off else second["confirmed"] - - icon = "🔴" if confirmed is True else "🟠" if confirmed is False else "🔴" - - disconnections.append({ - "time": f"{start_time}-{end_time}", - "icon": icon, - "queue": queue - }) - - i = j if j > i else i + 1 - else: - i += 1 + current_time = None + current_status = None + current_confirmed = None + current_queue = None + start_time = None - # Выводим сгруппированные отключения - if disconnections: - for d in disconnections: - queue_info = f" Ч{d['queue']}" if d['queue'] else "" - message += f"{d['icon']} {d['time']}{queue_info}\n" + disconnection_periods = [] + + for i, block in enumerate(day_blocks): + hour = block["hour"] + + # Проверяем обе половины часа + for half_idx, half in enumerate([block["first_half"], block["second_half"]]): + time_str = f"{hour:02d}:00" if half_idx == 0 else f"{hour:02d}:30" + + if half["status"] == "off": + if current_status != "off": + # Начало нового периода отключения + start_time = time_str + current_confirmed = half["confirmed"] + current_queue = half["queue"] + current_status = "off" + else: + if current_status == "off": + # Конец периода отключения + icon = "🔴" if current_confirmed is True else "🟠" + queue_text = f" • Черга {current_queue}" if current_queue else "" + disconnection_periods.append(f"{icon} {start_time} - {time_str}{queue_text}") + current_status = half["status"] + + # Если день закончился на отключении + if current_status == "off": + icon = "🔴" if current_confirmed is True else "🟠" + queue_text = f" • Черга {current_queue}" if current_queue else "" + next_hour = (day_blocks[-1]["hour"] + 1) % 24 + end_time = f"{next_hour:02d}:00" + disconnection_periods.append(f"{icon} {start_time} - {end_time}{queue_text}") + + # Выводим результат + if disconnection_periods: + for period in disconnection_periods: + message += period + "\n" else: - message += "🟢 Відключень немає\n" + message += "🟢 Відключень немає!\n" message += "\n" - message += "🔴 підтверджено • 🟠 можливо" + message += f"{'─' * 32}\n" + message += "💡 Легенда:\n" + message += "🔴 — Підтверджено\n" + message += "🟠 — Можливо\n" + message += "🟢 — Світло є\n" return message def format_single_day_schedule(schedule, day_num): - """Форматирует расписание для одного дня (еще более компактно)""" + """Форматирует расписание для одного дня""" start_date = datetime.now() date_str = (start_date + timedelta(days=day_num)).strftime('%d.%m') - day_name = "Сьогодні" if day_num == 0 else "Завтра" + day_name = "🌅 Сьогодні" if day_num == 0 else "🌄 Завтра" - message = f"⚡️ {day_name} ({date_str})\n\n" + message = f"{'─' * 32}\n" + message += f"📅 {day_name} ({date_str})\n" + message += f"{'─' * 32}\n\n" day_blocks = [b for b in schedule if b["day"] == day_num] if not day_blocks: - return message + "❓ Немає даних" + return message + "❓ Немає даних" - # Создаем почасовой график одной строкой - hourly = [] - for block in day_blocks: - first = block["first_half"] - second = block["second_half"] + # Создаем визуальную шкалу времени + message += "⏰ Часова шкала:\n\n" + + # Выводим по 4 часа в строке для читаемости + for start_hour in range(0, 24, 4): + line = "" + for h in range(start_hour, min(start_hour + 4, 24)): + block = day_blocks[h] if h < len(day_blocks) else None + if not block: + line += f"{h:02d}|❓❓ " + continue + + first = block["first_half"] + second = block["second_half"] + + def get_icon(half_data): + if half_data["status"] == "off": + return "🔴" if half_data["confirmed"] is True else "🟠" + elif half_data["status"] == "on": + return "🟢" + return "❓" + + first_icon = "●" if first["status"] == "off" else "·" + second_icon = "●" if second["status"] == "off" else "·" + + line += f"{h:02d}|{first_icon}{second_icon} " - def get_short_icon(half_data): - if half_data["status"] == "off": - return "●" if half_data["confirmed"] is True else "○" - elif half_data["status"] == "on": - return "·" - return "?" - - hour_str = f"{block['hour']:02d}" - first_icon = get_short_icon(first) - second_icon = get_short_icon(second) - hourly.append(f"{hour_str}|{first_icon}{second_icon}") + message += line + "\n" - # Выводим по 6 часов в строке - for i in range(0, len(hourly), 6): - chunk = hourly[i:i+6] - message += " ".join(chunk) + "\n" + message += "\n● = відключено, · = світло\n\n" - message += "\n● підтв • ○ можл • · світло\n\n" + # Детальний список відключень + message += "📋 Детально:\n\n" - # Добавляем список отключений - disconnections = [] + has_disconnections = False for block in day_blocks: + hour = block["hour"] first = block["first_half"] second = block["second_half"] if first["status"] == "off": + has_disconnections = True icon = "🔴" if first["confirmed"] is True else "🟠" - time = f"{block['hour']:02d}:00-30" - queue = f" Ч{first['queue']}" if first['queue'] else "" - disconnections.append(f"{icon} {time}{queue}") + status = "підтв." if first["confirmed"] is True else "можл." + queue = f"Ч{first['queue']}" if first['queue'] else "—" + message += f"{icon} {hour:02d}:00-{hour:02d}:30 • {status} • {queue}\n" if second["status"] == "off": + has_disconnections = True icon = "🔴" if second["confirmed"] is True else "🟠" - time = f"{block['hour']:02d}:30-{block['hour']+1:02d}:00" - queue = f" Ч{second['queue']}" if second['queue'] else "" - disconnections.append(f"{icon} {time}{queue}") + status = "підтв." if second["confirmed"] is True else "можл." + queue = f"Ч{second['queue']}" if second['queue'] else "—" + message += f"{icon} {hour:02d}:30-{hour+1:02d}:00 • {status} • {queue}\n" - if disconnections: - message += "Відключення:\n" + "\n".join(disconnections) - else: - message += "🟢 Відключень немає" + if not has_disconnections: + message += "🟢 Відключень немає!\n" + + message += f"\n{'─' * 32}\n" + message += "🔴 підтв. • 🟠 можл. • 🟢 світло" return message @@ -335,8 +339,7 @@ async def check_schedule(): message = format_schedule_message(new_schedule, days_to_show=2) if last_schedule is None: - # Первый запуск - await send_to_all_users(f"✨ Бот запущено!\n\n{message}") + pass # Ничего не делать, если изменений нет else: # Изменения в графике await send_to_all_users(f"🔄 Графік оновлено!\n\n{message}") @@ -530,7 +533,22 @@ async def cmd_check(message: Message): return await message.answer("🔄 Перевіряю графік...", reply_markup=get_main_keyboard()) - await check_schedule() + + # Выполняем проверку и отправляем результат + try: + html = get_voe_html(VOE_CITY_ID, VOE_STREET_ID, VOE_HOUSE_ID) + new_schedule = parse_html(html) + + if schedules_differ(last_schedule, new_schedule): + result_message = "✅ Знайдено зміни в графіку!\n\n" + result_message += format_schedule_message(new_schedule, days_to_show=2) + await message.answer(result_message, parse_mode="HTML", reply_markup=get_main_keyboard()) + else: + result_message = "✅ Графік без змін\n\n" + result_message += format_schedule_message(new_schedule, days_to_show=2) + await message.answer(result_message, parse_mode="HTML", reply_markup=get_main_keyboard()) + except Exception as e: + await message.answer(f"❌ Помилка при перевірці: {str(e)}", reply_markup=get_main_keyboard()) async def main():